关于javascript:关于微前端你想知道的都在这

35次阅读

共计 15180 个字符,预计需要花费 38 分钟才能阅读完成。

更多请关注微前端专题 https://codeteenager.github.io/Micro-Frontends/

介绍

微前端官网:https://micro-frontends.org/

问题:如何实现多个利用之间的资源共享?

之前比拟多的解决形式是 npm 包模式抽离和援用,比方多个利用我的项目之间,可能有某业务逻辑模块或其余是可复用的,便抽离进去以 npm 包的模式进行治理和应用。但这样却带来了以下几个问题:

  • 公布效率低下:如果须要迭代 npm 包内的逻辑业务,须要先公布 npm 包之后,再每个应用了该 npm 包的利用都更新一次 npm 包版本,再各自构建公布一次,过程繁琐。如果波及到的利用更多的话,破费的人力和精力就更多了。
  • 多团队合作容易不标准:蕴含通用模块的 npm 包作为共享资产,每个人领有它,但在实践中,这通常意味着没有人领有它,它很快就会充斥芜杂的格调不统一的代码,没有明确的约定或技术愿景。

这些问题让咱们意识到,扩大前端开发规模以便多个团队能够同时开发一个大型且简单的产品是一个重要但又辣手的难题。因而,早在 2016 年,微前端概念诞生了。

微前端概念

“微前端”一词最早于 2016 年底在 ThoughtWorks Technology Radar 中提出,它将后端的微服务概念扩大到了前端世界。微服务是服务端提出的一个有界上下文、松耦合的架构模式,具体是将利用的服务端拆分成更小的微服务,这些微服务都能独立运行,采纳轻量级的通信形式(比方 HTTP)。

微前端概念的提出能够借助上面的 Web 利用架构模式演变图来了解。

最原始的架构模式是单体 Web 利用,整个利用由一个团队来负责开发。

随着技术的倒退,开发职责开始细分,一个我的项目的负责团队会分化成前端团队和后端团队,即呈现了前后端拆散的架构形式。

随着我的项目变得越来越简单,先感触到压力的是后端,于是微服务的架构模式开始呈现。

随着前端运行环境进一步晋升,Web 利用的发展趋势越来越偏向于富利用,即在浏览器端集成更多的性能,前端层的代码量以及业务逻辑也开始快速增长,从而变得越来越难以保护。于是引入了微服务的架构思维,将网站或 Web 利用依照业务拆分成粒度更小的微利用,由独立的团队负责开发。

从图上能够看出,微前端、微服务这些架构模式的演变趋势就是一直地将逻辑进行拆分,从而升高我的项目复杂度,晋升可维护性和可复用性。

所以说微前端是一种相似于微服务的架构,是一种由独立交付的多个前端利用组成整体的架构格调,将前端利用分解成一些更小、更简略的可能独立开发、测试、部署的利用,而在用户看来依然是内聚的单个产品。有一个基座利用(主利用),来治理各个子利用的加载和卸载。

微前端的利用场景

从下面的演变过程能够看出,微前端架构比拟适宜大型的 Web 利用,常见的有以下 3 种模式。

  • 公司外部的平台零碎。这些零碎之间存在肯定的相关性,用户在应用过程中会波及跨零碎的操作,频繁地页面跳转或零碎切换将导致操作效率低下。而且,在多个独立零碎外部可能会开发一些反复度很高的性能,比方用户治理,这些反复的性能会导致开发成本和用户应用成本上升。
  • 大型单页利用。这类利用的特点是零碎体量较大,导致在日常调试开发的时候须要消耗较多工夫,重大影响到开发体验和效率。而且随着业务上的性能降级,我的项目体积还会一直增大,如果我的项目要进行架构降级的话革新老本会很高。
  • 对已有零碎的兼容和扩大。比方一些我的项目应用的是老旧的技术,应用微前端之后,对于新性能的开发能够应用新的技术框架,这样防止了颠覆重构,也防止了持续基于过期的技术进行开发。

微前端的架构模式

微前端架构按集成微利用的地位不同,次要能够分为 2 类:

  • 在服务端集成微利用,比方通过 Nginx 代理转发;
  • 在浏览器集成微利用,比方应用 Web Components 的自定义元素性能。

服务端集成

服务端集成罕用的形式是通过反向代理,在服务端进行路由转发,即通过门路匹配将不同申请转发到对应的微利用。这种架构形式实现起来比拟容易,革新的工作量也比拟小,因为只是将不同的 Web 利用拼凑在一起,严格地说并不能算是一个残缺的 Web 利用。当用户从一个微利用跳转到另一个微利用时,往往须要刷新页面从新加载资源。

这种代理转发的形式和间接跳转到对应的 Web 利用相比具备一个劣势,那就是不同利用之间的通信问题变得简略了,因为在同一个域下,所以能够共享 localstorage、cookie 这些数据。譬如每个微利用都须要身份认证信息 token,那么只须要登录后将 token 信息写入 localstorage,后续所有的微利用就都能够应用了,不用再从新登录或者应用其余形式传递登录信息。

浏览器集成

浏览器集成也称运行时集成,常见的形式有以下 3 种。

  • iframe。通过 iframe 的形式将不同的微利用集成到主利用中,实现成本低,但款式、兼容性方面存在肯定问题,比方沙箱属性 sandbox 的某些值在 IE 下不反对。
  • 前端路由。每个微利用暴露出渲染函数,主利用在启动时加载各个微利用的主模块,之后依据路由规定渲染相应的微利用。尽管实现形式比拟灵便,但有肯定的革新老本。
  • Web Components。基于原生的自定义元素来加载不同微利用,借助 Shadow DOM 实现隔离,革新老本比拟大。

这也是一种十分热门的集成形式,代表性的框架有 single-spa 以及基于它批改的乾坤。

微前端的劣势:

同步更新

比照 npm 包形式抽离,让咱们意识到更新流程和效率的重要性,微前端因为是多个子利用的聚合,如果多个业务利用依赖同一个服务利用的功能模块,只须要更新服务利用,其余业务利用就能够立马更新,从而缩短了更新流程和节约了更新老本。

增量降级

迁徙是一项十分耗时且艰巨的工作,比方有一个管理系统应用 AngularJS 开发保护曾经有三年工夫,然而随着工夫的推移和团队成员的变更,无论从开发成本还是用人需要上,AngularJS 曾经不能满足要求,于是团队想要更新技术栈,想在其余框架中实现新的需要,然而现有我的项目怎么办?间接迁徙是不可能的,在新的框架中齐全重写也不太事实。

应用微前端架构就能够解决问题,在保留原有我的项目的同时,能够齐全应用新的框架开发新的需要,而后再应用微前端架构将旧的我的项目和新的我的项目进行整合,这样既能够使产品失去更好的用户体验,也能够使团队成员在技术上失去提高,产品开发成本也降到的最低。

独立部署与公布

在目前的单页利用架构中,应用组件构建用户界面,利用中的每个组件或性能开发实现或者 bug 修复实现后,每次都须要对整个产品从新进行构建和公布,工作耗时操作上也比拟繁琐。

在应用了微前端架构后,能够将不能的功能模块拆分成独立的利用,此时功能模块就能够独自构建独自公布了,构建工夫也会变得十分快,利用公布后不须要更改其余内容利用就会自动更新,这意味着你能够进行频繁的构建公布操作了。

独立团队决策

因为微前端架构与框架无关,当一个利用由多个团队进行开发时,每个团队都能够应用本人善于的技术栈进行开发,也就是它容许适当的让团队决策应用哪种技术,从而使团队合作变得不再生硬。

如何实现微前端

  1. 多个微利用如何进行组合?

    在微前端架构中,除了存在多个微利用以外,还存在一个容器利用,每个微利用都须要被注册到容
    器利用中。

    微前端中的每个利用在浏览器中都是一个独立的 JavaScript 模块,通过模块化的形式被容器利用启
    动和运行。

    应用模块化的形式运行利用能够避免不同的微利用在同时运行时发生冲突。

  2. 在微利用中如何实现路由?

    在微前端架构中,当路由发生变化时,容器利用首先会拦挡路由的变动,依据路由匹配微前端应
    用,当匹配到微利用当前,再启动微利用路由,匹配具体的页面组件。

  3. 微利用与微利用之间如何实现状态共享?

    在微利用中能够通过公布订阅模式实现状态共享,比方应用 RxJS。

  4. 微利用与微利用之间如何实现框架和库的共享?

    通过 import-map 和 webpack 中的 externals 属性。

微前端落地计划

  1. 自组织模式:通过约定进行互调,但会遇到解决第三方依赖等问题
  2. 基座模式:通过搭建基座、配置核心来治理子利用。如基于 Single Spa 的乾坤计划,也有基于自身团队业务量身定制的计划。
  3. 去核心模式:脱离基座模式,每个利用之间都能够彼此分享资源,如基于 Webpack 5 Module Federation 实现的 EMP 微前端计划,能够实现多个利用彼此共享资源。

其中,目前值得关注的是去核心模式中的 EMP 微前端计划,既能够实现跨技术栈调用,又能够在雷同技术栈的利用间深度定制共享资源。

微前端的建设

微前端不是框架、不是工具 / 库,而是一套架构体系,它包含若干库、工具、中心化治理平台以及相干配套设施。

微前端包含 3 局部:

  • 微前端的基础设施。这是目前探讨得最多的,一个微利用如何通过一个组件基座加载进来、脚手架工具、怎么独自构建和部署、怎么联调。
  • 微前端配置核心:标准化的配置文件格式,反对灰度、回滚、红蓝、A/B 等公布策略。
  • 微前端的可察看性工具:对于任何分布式特点的架构,线上 / 线下治理都很重要。

微前端具体要解决好的 10 个问题:

  1. 微利用的注册、异步加载和生命周期治理
  2. 微利用之间、主从之间的音讯机制
  3. 微利用之间的平安隔离措施
  4. 微利用的框架无关、版本无关
  5. 微利用之间、主从之间的公共依赖的库、业务逻辑 (utils) 以及版本怎么治理
  6. 微利用独立调试、和主利用联调的形式,疾速定位报错(发射问题)
  7. 微利用的公布流程
  8. 微利用打包优化问题
  9. 微利用专有云场景的出包计划
  10. 渐进式降级:用微利用计划平滑重构老我的项目

微前端框架

  • Single-SPA:是一个用于前端微服务化的 JavaScript 前端解决方案,实现了路由劫持和利用加载,然而没有解决款式隔离,js 执行隔离。https://github.com/single-spa…
  • qiankun:基于 Single-SPA 提供了更多开箱即用的 API(single-spa + sandbox + import-html-entry),做到了与技术栈无关,并且接入简略,靠的是协定接入(子利用必须导出 bootstrap、mount、unmount 办法)。https://github.com/umijs/qiankun
  • 腾讯的无界:https://github.com/Tencent/wujie
  • 腾讯的 hel-micro:https://github.com/tnfe/hel
  • 美团的 Bifrost:Bifrost 微前端框架
  • 字节的 Garfish:https://github.com/modern-js-…
  • 阿里的 icestark:https://github.com/ice-lab/ic…
  • 京东的 MicroApp:https://zeroing.jd.com/micro-…
  • EMP:https://github.com/efoxTeam/emp
  • 阿里云:https://github.com/aliyun/ali…

实现微前端的几种形式:

  • 基于 single-spa 的 qiankun:single-spa 的实现原理是首先在基座利用中注册所有 App 的路由,single-spa 保留各子利用的路由映射关系,充当微前端控制器 Controler,URL 响应时,匹配子利用路由并加载渲染子利用。相比于 single-spa,qiankun 他解决了 JS 沙盒环境,不须要咱们本人去进行解决。在 single-spa 的开发过程中,咱们须要本人手动的去写调用子利用 JS 的办法(如下面的 createScript 办法),而 qiankun 不须要,乾坤只须要你传入响应的 apps 的配置即可,会帮忙咱们去加载。
  • 基于 WebComponent 的 micro-app:micro-app 并没有因循 single-spa 的思路,而是借鉴了 WebComponent 的思维,通过 CustomElement 联合自定义的 ShadowDom,将微前端封装成一个类 WebComponent 组件,从而实现微前端的组件化渲染。并且因为自定义 ShadowDom 的隔离个性,micro-app 不须要像 single-spa 和 qiankun 一样要求子利用批改渲染逻辑并暴露出办法,也不须要批改 webpack 配置,是目前市面上接入微前端老本最低的计划。
  • 基于 webpack5 提供的 Module Federation 的 hel-micro 框架:Module Federation 是 Webpack5 提出的概念,module federation 用来解决多个利用之间代码共享的问题,让咱们更加优雅的实现跨利用的代码共享。
  • 基于 iframe 的 wujie

iframe 计划

qiankun 技术圆桌中有一篇对于微前端 Why Not Iframe 的思考,次要有以下几点:

  • iframe 提供了浏览器原生的硬隔离计划,不论是款式隔离、js 隔离这类问题通通都能被完满解决。
  • url 不同步。浏览器刷新 iframe url 状态失落、后退后退按钮无奈应用。
  • UI 不同步,DOM 构造不共享。设想一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时咱们要求这个弹框要浏览器居中显示,还要浏览器 resize 时主动居中..
  • 全局上下文齐全隔离,内存变量不共享。iframe 内外零碎的通信、数据同步等需要,主利用的 cookie 要透传到根域名都不同的子利用中实现免登成果。
  • 慢。每次子利用进入都是一次浏览器上下文重建、资源从新加载的过程。

因为这些起因,最终大家都舍弃了 iframe 计划。

Web Component

MDN Web Components 由三项次要技术组成,它们能够一起应用来创立封装性能的定制元素,能够在你喜爱的任何中央重用,不用放心代码抵触。

  • Custom elements(自定义元素):一组 JavaScript API,容许您定义 custom elements 及其行为,而后能够在您的用户界面中依照须要应用它们。
  • Shadow DOM(影子 DOM):一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 离开出现)并管制其关联的性能。通过这种形式,您能够放弃元素的性能公有,这样它们就能够被脚本化和款式化,而不必放心与文档的其余局部发生冲突。
  • HTML templates(HTML 模板):<template><slot> 元素使您能够编写不在出现页面中显示的标记模板。而后它们能够作为自定义元素构造的根底被屡次重用。

然而兼容性很差,查看 can i use WebComponents。

single-spa 微前端计划

spa 单页利用时代,咱们的页面只有 index.html 这一个 html 文件,并且这个文件外面只有一个内容标签 <div id=”app”></div>,用来充当其余内容的容器,而其余的内容都是通过 js 生成的。也就是说,咱们只有拿到了子项目的容器 <div id=”app”></div> 和生成内容的 js,插入到主我的项目,就能够呈现出子项目的内容。

<link href=/css/app.c8c4d97c.css rel=stylesheet>
<div id=app></div>
<script src=/js/chunk-vendors.164d8230.js> </script>
<script src=/js/app.6a6f1dda.js> </script>

咱们只须要拿到子项目的下面四个标签,插入到主我的项目的 HTML 中,就能够在父我的项目中展现出子项目。

这里有个问题,因为子项目的内容标签是动静生成的,其中的 img/video/audio 等资源文件和按需加载的路由页面 js/css 都是相对路径,在子项目的 index.html 外面,能够正确申请,而在主我的项目的 index.html 外面,则不能。

举个例子,假如咱们主我的项目的网址是 www.baidu.com,子项目的网址是 www.taobao.com,在子项目的 index.html 外面有一张图片 <img src="./logo.jpg">,那么这张图片的残缺地址是 www.taobao.com/logo.jpg,当初将这个图片的 img 标签生成到了父我的项目的 index.html,那么图片申请的地址是 www.baidu.com/logo.jpg,很显然,父我的项目服务器上并没有这张图。

解决思路:

  1. 这外面的 js/css/img/video 等都是相对路径,是否通过 webpack 打包,将这些门路全副打包成绝对路径?这样就能够解决文件申请失败的问题。
  2. 是否手动(或借助 node)将子项目的文件全副拷贝到主我的项目服务器上,node 监听子项目文件有更新,就主动拷贝过去,并且按 js/css/img 文件夹合并
  3. 是否像 CDN 一样,一个服务器挂了,会去其余服务器上申请对应文件。或者说服务器之间的文件共享,主我的项目上的文件申请失败会主动去子服务器上找到并返回。

通常做法是动静批改 webpack 打包的 publicPath,而后就能够主动注入前缀给这些资源。

single-spa 是一个微前端框架,基本原理如上,在上述出现子项目的根底上,还新增了 bootstrap、mount、unmount 等生命周期。

绝对于 iframe,single-spa 让父子我的项目属于同一个 document,这样做既有益处,也有害处。益处就是数据 / 文件都能够共享,公共插件共享,子项目加载就更快了,毛病是带来了 js/css 净化。

single-spa 上手并不简略,也不能开箱即用,开发部署更是须要批改大量的 webpack 配置,对子我的项目的革新也十分多。

运行时框架

运行时框架次要做了以下这些事:

  • 利用加载 – 依据注册的子利用,通过给定的 url,加载约定格局的子利用入口,并挂载到给定地位
    局部框架是依据相似 manifest 的数据,来获取子利用注册状况以及入口地址
    局部框架反对和治理平台配合,运行时承受平台动静注入的入口地址 (也有框架声称运行时注入和治理平台解耦,但理论是如果不必,就得本人实现注入逻辑)
    JS 做入口更纯正,用 HTML 做入口更易于旧我的项目革新
    业界目前罕用两种入口格局,HTML 和 JS
    父子入口组合 (即确定依赖关系) 也有两种模式,构建时组合 和 运行时组合
  • 生命周期 – 加载 / 挂载 / 更新 / 卸载 等
    加载 / 挂载时做的初始化、权限守卫、i18n 语言等
    卸载时做清理,如卸载 script 标签、style 标签、子利用 dom 等
    以及路由、父子通信时做双向更新的桥梁
  • 路由同步 – 子利用的路由切换时,同步更新 url;url 跳转 / 更新时,同步更新子利用
    也就是对子利用做到路由等同于 url
  • 利用通信 – 是说反对父子利用之间便捷地互相通信,不像 postMessage 那样难用 (指字符串)
    什么。
  • 沙箱隔离 – 为了各个利用「互补烦扰」,须要把各个利用在“隔离”的环境中执行
    短少隔离的话,CSS 全局款式可能 抵触凌乱,JS 全局变量可能被 净化 / 篡改 / 替换。
  • 异样解决 – 以上所有货色在报错时的对立解决,比方加载失败、或者路由匹配失败

利用加载

App Entry 作为子利用的加载入口,微前端框架依据注册的子利用,通过给定的 url,加载约定格局的子利用入口,并挂载到给定地位,目前业内有两种 entry: JS Entry 和 Html Entry。

阐明 长处 毛病
html html 作为子利用入口 解耦更彻底,子利用不依赖于主利用 DOM,子利用独立开发,独立部署 多了一次对 html 的申请,解析有性能损耗,无奈做构建时优化
js js 作为子利用入口 便于做构建时优化 依赖主利用提供挂载节点,打包产物体积收缩,资源无奈并行加载

Js Entry 的毛病是:

  • 子利用更新打包后的 js bundle 名称会变动,主利用须要保障每次获取都是最新的 js bundle
  • 子利用所有资源打包到一个文件中,会失去 css 提取、动态资源并行加载、首屏加载 (体积微小) 等优化。
  • 须要在子利用打包过程中,批改相应的配置以补全子利用 js 资源的门路。

而 Html Entry 只须要指定子利用的 html 入口即可,微前端框架在加载 html 字符串后,从中提取出 css、js 资源,运行子利用时,装置款式、执行脚本,运行脚本中提供的生命周期钩子。因而长处也很显著:

  • 无需关怀利用打包后的 js 名称变动的问题。
  • 依然能够享受 css 提取、动态资源并行加载(外部应用 Promise.all 并行发出请求资源)、首屏加载等优化。
  • 申请资源时,主动补全资源门路。

JS Entry

JS Entry 的形式通常是子利用将资源打成一个 entry script,要求子利用的所有资源打包到一个 js bundle 里,包含 css、图片等资源。像 single-spa 通常是联合 SystemJS 来实现,在 single-spa 框架中,基座会检测浏览器 url 的变动,在变动时往往通过 SystemJS 的 import 映射,来加载不同的子利用 js。

Import maps

Import maps 这个性能是 Chrome 89 才反对的。它是对 import 的一个映射解决,让你管制在 js 中应用 import 时,到底从哪个 url 获取这些库。

比方通常咱们会在 js 中,以上面这种形式引入模块:

import moment from "moment"

失常状况下必定是 node_modules 中引入,然而当初咱们在 html 中退出上面的代码:

<script type="importmap">
    {
        "imports": {"moment": "/moment/src/moment.js"}
    }
</script>

这里 /moment/src/moment.js 这个地址换成一个 cdn 资源也是能够的。最终达到的成果就是:

import moment from "/moment/src/moment.js"

有了 Import maps,import 的语法就能够间接在浏览器中应用,而不再须要 webpack 来帮咱们进行解决,不须要从 node_modules 中去加载库。

Import maps 甚至还有一个兜底的玩法:

"imports": {
        "jquery": [
            "https:// 某 CDN/jquery.min.js",
            "/node_modules/jquery/dist/jquery.js"
        ]
}

当 cdn 有效时,再从本地库中获取内容。

只管 Import maps 十分弱小,然而毕竟浏览器兼容性还并不是很好,所以就有了咱们的 polifill 计划:SystemJS

SystemJS

SystemJs 是一个通用的模块加载器,有属于本人的模块化标准。他能在浏览器和 node 环境上动静加载模块,微前端的外围就是加载子利用,因而将子利用打包成模块,在浏览器中通过 SystemJs 来加载模块。SystemJS 可兼容到 IE11,然而它对于插件版本要求十分严格,而且变动十分大,兼容性也不是特地好,应用体验也不是很好,所以目前实际中用的非常少。它同样反对 import 映射,然而它的语法稍有不同:

<script src="system.js"></script>
<script type="systemjs-importmap">
{
    "imports": {"lodash": "https://unpkg.com/lodash@4.17.10/lodash.js"}
}
</script>

在浏览器中引入 system.js 后,会去解析 type 为 systemjs-importmap 的 script 下的 import 映射。

Html Entry

HTML Entry 是由 import-html-entry 库实现的,这个库次要做了这些事件:

  1. 加载 entry html (index.html) 的内容到内存。
  2. 将 entry html 中的 css、js、link 等标签下的内容获取进去(蕴含内部的和内联的),整顿成网页所需的 js、css 列表。并将无用标签去掉(例如正文、ignore 等)。
  3. 加载所有外链 js 脚本,并将这些外链 js 和内联 js 一起整顿为 script list。
  4. 加载所有外链 css 文件,并将其以内联 (<style/>) 的形式插入到 entry html 中。
  5. 将解决后的 entry html 和待执行的 script list 返回给调用方(基座)。

通过 http 申请加载指定地址的首屏内容即 html 页面,而后解析这个 html 模版失去 template, scripts , entry, styles。

{
  template: 通过解决的脚本,link、script 标签都被正文掉了,
  scripts: [脚本的 http 地址 或者 { async: true, src: xx} 或者 代码块],
  styles: [款式的 http 地址],
     entry: 入口脚本的地址,要不是标有 entry 的 script 的 src,要不就是最初一个 script 标签的 src
}

而后近程加载 styles 中的款式内容,将 template 模版中正文掉的 link 标签替换为相应的 style 元素。

而后向外裸露一个 Promise 对象

{
  // template 是 link 替换为 style 后的 template
    template: embedHTML,
    // 动态资源地址
    assetPublicPath,
    // 获取内部脚本,最终失去所有脚本的代码内容
    getExternalScripts: () => getExternalScripts(scripts, fetch),
    // 获取内部款式文件的内容
    getExternalStyleSheets: () => getExternalStyleSheets(styles, fetch),
    // 脚本执行器,让 JS 代码 (scripts) 在指定 上下文 中运行
    execScripts: (proxy, strictGlobal) => {if (!scripts.length) {return Promise.resolve();
        }
        return execScripts(entry, scripts, proxy, { fetch, strictGlobal});
    }
}

在 import-html-entry 库解决完之后,基座在须要的加载子利用时候将这个 html 放到对应的 DOM 容器节点,并执行 script list,即实现子利用的加载。

理解更多

  • HTML Entry 源码剖析
  • 揭开 import-html-entry 面纱

路由同步

子利用注册的时候,提供子利用激活规定 (路由字符串 或 函数)。因而,监听 hashchange 和 popstate 事件,在事件回调函数中,依据注册的子利用激活规定,卸载 / 激活子利用。

以 Vue-Router 的 history 模式为例,在切换路由时,通常会做三件重要事件:执行一连串的 hook 函数、更新 url、router-view 更新,其中更新 url,就是通过 pushState/replaceState 的模式实现的。因而重写并加强 history.pushState 和 history.replaceState 办法,在执行它们的时候,能够拿到执行前、执行后的 url,比照是否有变动,如果有,依据注册的子利用激活规定,卸载 / 激活子利用。

以 single-spa 为例:

  // We will trigger an app change for any routing events.
  window.addEventListener("hashchange", urlReroute);
  window.addEventListener("popstate", urlReroute);

  // Monkeypatch addEventListener so that we can ensure correct timing
  const originalAddEventListener = window.addEventListener;
  const originalRemoveEventListener = window.removeEventListener;
  window.addEventListener = function (eventName, fn) {if (typeof fn === "function") {
      if (routingEventsListeningTo.indexOf(eventName) >= 0 &&
        !find(capturedEventListeners[eventName], (listener) => listener === fn)
      ) {capturedEventListeners[eventName].push(fn);
        return;
      }
    }

    return originalAddEventListener.apply(this, arguments);
  };

  window.removeEventListener = function (eventName, listenerFn) {if (typeof listenerFn === "function") {if (routingEventsListeningTo.indexOf(eventName) >= 0) {capturedEventListeners[eventName] = capturedEventListeners[eventName].filter((fn) => fn !== listenerFn);
        return;
      }
    }

    return originalRemoveEventListener.apply(this, arguments);
  };

  window.history.pushState = patchedUpdateState(
    window.history.pushState,
    "pushState"
  );
  window.history.replaceState = patchedUpdateState(
    window.history.replaceState,
    "replaceState"
  );

以上次要是减少了 hashchange、popstate 两个监听,监听 url 的变动。同时重写 pushState 以及 replaceState 办法,在办法中调用原有办法后执行如何解决子利用的逻辑监听 hashchange 及 popstate 事件,事件触发后执行如何解决子利用的逻辑。

生命周期

single-spa 的一个关键点就是生命周期,子利用生命周期蕴含 bootstrap,mount,unmount 三个回调函数。主利用在治理子利用的时候,通过子利用裸露的生命周期函数来实现子利用的启动和卸载。

  • load:当利用匹配路由时就会加载脚本(非函数,只是一种状态)。
  • bootstrap:利用内容首次挂载到页背后调用。
  • Mount:当主利用断定须要激活这个子利用时会调用, 实现子利用的挂载、页面渲染等逻辑。
  • unmount:当主利用断定须要卸载这个子利用时会调用, 实现组件卸载、清理事件监听等逻辑。
  • unload:非必要函数,个别不应用。unload 之后会重新启动 bootstrap 流程。

沙箱隔离

子利用和基座的隔离次要有两点:

  • 款式隔离
  • js 隔离

款式隔离

shadowDOM

目前相对来说应用最多的款式隔离机制

BEM、CSS Modules

BEM:Block Element Module 命名束缚

  • B:Block 一个独立的模块,一个自身就有意义的独立实体,比方:header、menu、container
  • E:Element 元素,块的一部分然而本身没有独立的含意,比方:header title、container input
  • M:Modifier 修饰符,块或者元素的一些状态或者属性标记,比方:small、checked
模块:.Block 
模块多单词:.Header-Block 
模块_状态:.Block_Modifier 
模块__子元素:.Block__Element 
模块__子元素_状态:.Block__Element_Modifier

CSS Modules:
代码中的每一个类名都是引入对象的一个属性,通过这种形式,即可在应用时明确指定所援用的 css 款式。并且 CSS Modules 在打包的时候会主动将类名转换成 hash 值,齐全杜绝 css 类名抵触的问题;

CSS in JS

应用 JS 写 CSS,也是目前比拟支流的计划,齐全不须要些独自的 css 文件,所有的 css 代码全副放在组件外部,以实现 css 的模块化,但对于历史代码不好解决

postcss

应用 postcss,在全局对所有 class 增加对立的前缀,然而在编译时解决,会减少编译工夫;

JS 隔离

js 隔离的外围是在基座和子利用中应用不同的上下文 (global env),从而达成基座和子利用之间 js 运行互不影响。

简略来说,就是给子利用独自的 window,防止对基座的 window 造成净化。

qiankun 的沙箱机制

qiankun 在 js 隔离上,同样提供了 3 种计划,别离是:

  1. LegacySandbox – 传统 js 沙箱,目前已弃用,须要配置 sandbox.loose = true 开启。此沙箱应用 Proxy 代理子利用对 window 的操作,将子利用对 window 的操作同步到全局 window 上,造成侵入。但同时会将期间对 window 的新增、删除、批改操作记录到沙箱变量中,在子利用敞开时销毁,再依据记录将 window 还原到初始状态。
  2. ProxySandbox – 代理 js 沙箱,非 IE 浏览器默认应用此沙箱。和 LegacySandbox 同样基于 Proxy 代理子利用对 window 的操作,和 LegacySandbox 不同的是,ProxySandbox 会创立一个虚构的 window 对象提供给子利用应用,哪怕是在运行时,子利用也不会侵入对 window,实现齐全的隔离。
  3. SnapshotSandbox – 快照 js 沙箱,IE 浏览器默认应用此沙箱。因为 IE 不反对 Proxy。此沙箱的原理是在子利用启动时,创立基座 window 的快照,存到一个变量中,子利用的 window 操作本质上是对这个变量操作。SnapshotSandbox 同样会将子利用运行期间的批改存储至 modifyPropsMap 中,以便在子利用创立和销毁时还原。
基于 iframe 的沙箱机制

iframe 标签能够发明一个独立的浏览器级别的运行环境,该环境与主环境隔离,并有本人的 window 上下文;在通信机制上,也能够利用 postMessage 等 API 与宿主环境进行通信。具体来说,在执行 JavaScript 代码上,有以下要求:

  1. 利用间运行时隔离:常见的是应用 shadowDOM 创立的款式隔离 DOM,再应用 Proxy 拦挡 JS 的执行,代理到 shadowDOM 所创立的隔离开的 DOM 上;
  2. 利用间通信:同域:window.parent,不同域:postMessage;或者 eventBus 等自定义的形式实现;
  3. 路由劫持:

    • a. 让 JavaScript 沙箱内路由变更操作在主应用环境失效:但这种对于相对路径的配置,如接口申请解决太繁琐,个别不倡议;
    • b. 同步沙箱内路由变动至主应用环境:监听 iframe 路由上下文,同步到主利用路由上,如 wujie;

利用通信

  • 基于 URL 来进行数据传递,然而传递音讯能力弱
  • 基于 CustomEvent 实现通信
  • 基于 props 奴才利用间通信
  • 应用全局变量、Redus 进行通信

异样解决

当运行中产生谬误时,须要对其进行捕捉,这里次要监听了 error 和 unhandledrejection 两个谬误事件。

window.addEventListener('error', errorHandler);
window.addEventListener('unhandledrejection', errorHandler);

相干文章

  • 微前端在平台级管理系统中的最佳实际
  • 微前端在银行零碎中的实际
  • 如何落地微前端一体化经营工作台
  • 微前端在金融的实际利用
  • 字节跳动是如何落地微前端的
  • 基于 iframe 的微前端框架 —— 擎天
  • 百度对于 EMP 的摸索:落地生产可用的微前端架构
  • 微前端在得物客服域的实际 | 那么多微前端框架,为啥咱们选 Qiankun + MF
  • 京东出品微前端框架 MicroApp 介绍与落地实际
  • 爱番番微前端框架落地实际
  • 推开“微前端”的门
  • 如何设计实现微前端框架 -qiankun
  • 指标是最欠缺的微前端解决方案 – qiankun 2.0
  • iframe 接班人 - 微前端框架 qiankun 在中后盾零碎实际
  • 微前端在网易七鱼的实际
  • 微前端在美团外卖的实际
  • 网易严选企业级微前端解决方案与落地实际
  • 有赞美业微前端的落地总结
  • 前端搞微前端 | 伟林 – 如何分三步施行微前端
  • 一体化微前端研发平台的摸索和实际
  • 美团质效工具产品的微前端实际
  • 从场景倒推咱们要什么样的微前端体系
  • 微前端架构体系思考与实际
  • 开源 | 携程度假零老本微前端框架 - 零界
  • 微前端在网易 LOFTER 中后盾业务中的实际(一)——微前端沙箱及微前端利用平台
  • 微前端在民生 APaaS/PSET 平台的摸索与实际
  • 微前端技术在游戏平台后盾零碎的实际
  • ICE 在微前端的摸索
  • 细述字节的微前端体系
  • 飞马 - 中后盾微前端页面搭建平台
  • 手把手教你定制一套适宜团队的微前端体系
  • 微前端如何帮忙咱们专一业务需要
  • 面向大型工作台的微前端解决方案 icestark
  • 前端搞微前端 | 鲲尘 – 如何在大型利用中架构设计微前端计划
  • 前端搞微前端 | 时光 – 如何在字节设计与实际微前端沙盒
  • 规范微前端架构在蚂蚁的落地实际
  • 云前端新物种 - 微前端体系
  • 拥抱云时代的前端开发架构—微前端
  • 每日优鲜供应链前端团队微前端革新
  • 微前端技术原理
  • 基于 qiankun 的微前端利用实际
  • 浅谈微前端在滴滴车服中的利用实际
  • 如何设计微前端中的奴才路由调度
  • 如何“取巧”实现一个微前端沙箱?
  • Garfish 微前端实现原理
  • 快手经营中台的通用微前端治理框架(上)
  • 快手经营中台的通用微前端治理框架(下)
  • 微前端技术在商羚商户端的实战
  • 如何打造跨团队微前端生态
  • 模块联邦 sdk 化微模块计划 – hel-micro
  • 基于 MF 的组件化共享工作流
  • 企业级微前端研发模式的摸索
  • 阿里本地生存微利用那些事
  • 模块联邦在微前端架构中的实际

正文完
 0