一、微前端现状
1.1 来源
微前端的概念是由ThoughtWorks在2016年提出的,它借鉴了微服务的架构理念,核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用,同时可以解决一些iframe的潜在问题。
1.2 意义
不可否认,微前端的设计起源于后端微服务,很多前端比较新的概念都来自于后端,比如Typescript,DDD领域驱动设计等等。
搞前端嘛,不折腾也不叫前端
微前端解决方案也给我们提供如下特性:
- 单个前端部分可独立开发、测试和部署;
- 无需重新构建即可添加、移除或替换单个前端部分;
- 不同的前端部分可使用不同的技术构建;
- 解决iframe硬隔离的一些问题
目前工作中遇到的项目有50%都已经应用上了该技术,这也体现了微前端带来的价值。
1.3 架构万能图
二、主流实现方案
2.1 qiankun为代表
- demo可参考:qiankun 使用方式代码Github
主应用
- 只有主应用需要安装qiankun,子应用不需要
- name,entry,container,activeRule,当判断页面路由匹配到activeRule时,就去动态创建script,把entry中的文件加载出来,因为子应用mount生命周期判断了渲染的#app,所以就可以把内容渲染到自定义的container中
- activeRule 则是和 window.location.pathname匹配,通过一级路由标识子应用
子应用接入
- 判断是否有传入的container,来判断是要渲染到主应用的#app,还是自己的#app
- 需要打包成一个umd格式的库:为了能通过window['app-name1']拿到子应用声明的生命周期,配合子应用的export的生命周期
实现原理
1、监视路由
- window.location.pathname等相关变化,触发接下来的匹配逻辑
2、匹配子应用
- 重写路由window.popState, replaceState,在保留原有功能的基础上,增加子应用entry映射相关逻辑
- 针对变化的路由,匹配子应用
3、加载子应用
- import-html-entry 解析入口文件中的html 和 script
- 动态创建script去执行jsCode
- 通过umd模块获取子应用,调用子应用mount方法,render子应用
4、渲染子应用
- 把js和html,渲染到提前预留的#app容器中
2.2 以无界为代表
webComponent
- 浏览器原生API,实现方式这里copy一下小满的:了解webComponents
- 通过webComponent动态加载子应用,目前主流用同样方案的是无界和Micro App
window.onload = () => {
class WuJie extends HTMLElement {
constructor() {
super()
this.init()
this.getAttr('url')
}
init() {
const shadow = this.attachShadow({ mode: "open" }) //开启影子dom 也就是样式隔离
const template = document.querySelector('#wu-jie') as HTMLTemplateElement
console.log(template);
shadow.appendChild(template.content.cloneNode(true))
}
getAttr (str:string) {
console.log('获取参数',this.getAttribute(str));
}
//生命周期自动触发有东西插入
connectedCallback () {
console.log('类似于vue 的mounted');
}
//生命周期卸载
disconnectedCallback () {
console.log('类似于vue 的destory');
}
//跟watch类似
attributeChangedCallback (name:any, oldVal:any, newVal:any) {
console.log('跟vue 的watch 类似 有属性发生变化自动触发');
}
}
window.customElements.define('wu-jie', WuJie)
}
沙箱
- 和qiankun sanbox沙箱不同的是,无界采用iframe做为沙箱方案,micro-app 0.x版本采用with沙箱,1.x支持vite后,支持和无界同样的ifame沙箱。
2.3 以webpack-module-federation为代表
模块联邦主要是一种去中心化的思想,也可以用来做服务拆分, 实现原理比较复杂,主要涉及到以下几个方面:
1. 模块接口定义
在需要共享的模块中,通过 module.exports 或 export 将需要共享的模块封装成一个模块接口,并将其在模块系统中注册。
2. 共享模块的描述信息
在需要共享模块的应用程序中,通过使用 ModuleFederationPlugin 插件,将需要共享的模块的描述信息以 JSON 格式写入配置中。描述信息包括需要共享的模块名称、模块接口、提供共享模块的应用程序的 URL 等。
3. 共享模块的加载
在需要使用共享模块的应用程序中,通过 webpack 的 container 远程加载共享模块的代码,并将其封装成一个容器。容器在当前应用程序中的作用是在容器中运行共享模块的代码,并按照描述信息将导出的模块接口暴露出来。容器本身是一个 JavaScript 运行时环境,它可以在需要使用共享模块的应用程序中被动或主动加载。
4. 远程模块的执行
在容器中加载共享模块的代码后,容器需要将其执行,并将执行过程中产生的模块接口导出。为了实现这个目的,容器会利用 webpack 打包时在编译过程中生成的一个特殊的运行时代码,即 remoteEntry.js,通过 script 标签远程加载到当前应用程序中。在这个特殊的运行时代码中,会封装一些与容器通信的方法,例如 remote 方法,可以用于按需加载模块、获取模块接口等。
综上,webpack-module-federation 基于这些原理,实现了多个独立的应用程序之间的模块共享和远程加载,从而可以实现高度解耦、可扩展的架构。
三、各大框架
3.1 Single-Spa
作为比较早的微前端框架,single-spa只是实现了加载器、路由托管。沙箱隔离并没有实现。最早在第一家公司,项目就采用基于single-spa定制的一套自己框架,主要是也是通过proxySandBox实现沙箱隔离。
3.2 QianKun
QianKun 基于 single-spa ,阿里系开源的微前端框架,应该也是大家接触最多的了,社区比较活跃,这点比较重要。
QianKun对single-spa方案进行完善,主要的完善点:
- 子应用资源由 js 列表修改进为一个
url,大大减轻注册子应用的复杂度 - 实现应用隔离,完成
js隔离方案 (window工厂) 和css隔离方案 (类vue的scoped) - 增加资源预加载能力,预先子应用
html、js、css资源缓存下来,加快子应用的打开速度
- 2.x 不支持Vite,由于实现原理冲突,很难支持,参考Issue:想问一下,未来是否考虑支持 vite #1257
- qiankun 3.0加入了支持,刚开始alpha阶段:github.com/umijs/qiank… 感谢评论区@佩子 的提醒
3.3 Mirco-App
Micro App 是京东出的一款基于 Web Component 原生组件进行渲染的微前端框架,不同于目前流行的开源框架,它从组件化的思维实现微前端,旨在降低上手难度、提升工作效率。
-
官方demo:Micro App
-
正式版本0.8版本, 1.0版本还是beta阶段,但是维护者在issue比较活跃
- 0.x版本 vite支持不是很好,使用的时候需要关闭沙箱
- 1.x版本 支持vite,需要采用iframe沙箱模式,这点和wujie的方案一样了,都是webComponent + iframe
接入
// 主 main.js
import microApp from "@micro-zoe/micro-app";
microApp.start();
// 加载子应用 a.js
import React from "react";
export default function AReact() {
return <micro-app name="ARact" url="http://127.0.0.1:4173/" iframe></micro-app>;
}
3.4 无界
- 官方demo:无界react-demo展示
无界是腾讯推出的一款微前端解决方式。它是一种基于 Web Components + iframe 的全新微前端方案,继承iframe的优点,补足 iframe 的缺点,让 iframe 焕发新生。
Web Components 是一个浏览器原生支持的组件封装技术,可以有效隔离元素之间的样式,iframe 可以给子应用提供一个原生隔离的运行环境,相比自行构造的沙箱 iframe 提供了独立的 window、document、history、location,可以更好的和外部解耦
接入
- 安装WujieReact即可,引入组件即可,和micro-app类似
<WujieReact
width="100%"
height="100%"
name="xxx"
url={xxx}
sync={true}
fetch={fetch}
props={props}
beforeLoad={beforeLoad}
beforeMount={beforeMount}
afterMount={afterMount}
beforeUnmount={beforeUnmount}
afterUnmount={afterUnmount}
>
</WujieReact>
3.5 Garfish
Garfish 是由字节跳动开源的一套微前端解决方案,主要用于解决现代 web 应用在前端生态繁荣和 web 应用日益复杂化两大背景下带来的 跨团队协作、技术体系多样化、应用日益复杂化等问题,Garfish 已经经过大量的线上应用的打磨和测试,功能稳定可靠。
框架特性:
-
🌈 丰富高效的产品特征
- Garfish 微前端子应用支持任意多种框架、技术体系接入
- Garfish 微前端子应用支持「独立开发」、「独立测试」、「独立部署」
- 强大的预加载能力,自动记录用户应用加载习惯增加加载权重,应用切换时间极大缩短
- 支持依赖共享,极大程度的降低整体的包体积,减少依赖的重复加载
- 内置数据收集,有效的感知到应用在运行期间的状态
- 支持多实例能力,可在页面中同时运行多个子应用提升了业务的拆分力度
-
📦 高扩展性的核心模块
- 通过 Loader 核心模块支持 HTML entry、JS entry 的支持,接入微前端应用简单易用
- Router 模块提供了路由驱动、主子路由隔离,用户仅需要配置路由表应用即可完成自主的渲染和销毁,无需关心内部逻辑
- Sandbox 模块为应用的 Runtime 提供运行时隔离能力,能有效隔离 JS、Style 对应用的副作用影响
- Store 提供了一套简单的通信数据交换机制
-
🎯 高度可扩展的插件机制
- 提供业务插件满足各种定制需求
设计理念:
另外字节跳动开源的号称
「现代 Web 工程体系」的 modern.js 也是解决微前端的一个框架,大家感兴趣的可以去官网查看,因为该框架是一整套工程体系解决方案,所以可酌情选择;
四、如何选择
-
自由度更高:module-federation
- 需要自定义实现css隔离、js沙箱、路由劫持等功能
-
用的最多:qiankun
- 相对比较成熟,社区活跃
- webpack体系、接入相对比较重
-
如果你的项目是一个后台管理系统且都是最新的 MV* 框架,那么
Qiankun、Micro App、Garfish、Liugi、Piral你都可以选择; -
如果你的项目需要聚合某些老旧框架(jQuery)的项目,那么
Qiankun、MicroApp都可选择,可优先考虑Micro App;