微服务
- 容器化,WSL,开发环境和部署环境完全一致,迁徙无成本。 devops
- 颗粒度可控和解耦。
- 异构的需求
微前端
微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
各个前端应用还可以独立运行、独立开发、独立部署。
微前端不是单纯的前端框架或者工具,而是一套架构体系,这个概念最早在2016年底被提出。
微前端的应用前提和使用场景
前提:
- SPA架构,单页面应用
- 前端异构
场景: 主要用于项目的拆分和细化;或者历史系统的整合
微前端的早期解决方案
微前端的模块组成
基座应用 + 微应用(一般微应用之间不直接通信,而是通过基座订阅/触发,然后将结果下发给微应用)
使用微前端的结果展示
使用微前端要解决的主要问题
- 应用加载
根据注册的子应用,通过给定的url,加载约定格式的子应用入口,并挂载到给定位置。入口方式通常有两种:HTML和JS,JS做入口更纯粹,HTML做入口更易于旧项目改造。此外,提供预加载功能也很有必要,预加载是指在应用尚未渲染时提前加载资源并缓存,从而提升首屏渲染速度
- 应用的生命周期
加载、挂载、更新、卸载
- 路由同步
子应用的路由切换时,同步更新url;url改变时,同步更新子应用
- 主微应用的隔离问题
js隔离
- Snapshot
子应用挂载时,先对全局window变量打个快照放闭包里,再把全局window交给子应用,在子应用卸载时通过快照恢复全局window变量- Sandbox
Proxy提供的是with和new Function闭包中用到的充当window作用域的对象,通过白名单属性限制能访问真正window上的部分元素,同时对document、history、location进行劫持限制等,从而组成沙箱环境
css隔离
命名约定、自动Scope、Shadow DOM、自动卸载、弹窗遮罩
- 通信问题
局部通信和全局通信
微前端架构介绍
qiankun(运行时确定依赖关系)
qiankun 方案是蚂蚁金服开源的一款框架,基于 single-spa 的微前端方案
特点
- html entry 的方式引入子应用,相比 js entry 极大的降低了应用改造的成本;
- 完备的沙箱方案,js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案,css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 两套适用不同场景的方案;
- 做了静态资源预加载能力;
不足
- 适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作;
- css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重;
- 无法同时激活多个子应用,也不支持子应用保活;
- 无法支持 vite 等 esmodule 脚本运行;
在主应用中注册微应用,当微应用信息注册完之后,一旦浏览器的url发生变化,便会自动触发qiankun的匹配逻辑,所有activeRule规则匹配上的微应用就会被插入到指定的container中,同时依次调用微应用暴露出的生命周期钩子(bootstrap、mount、unmount)
使用
qiankun官网
- 安装qiankun,$ yarn add qiankun # 或者 npm i qiankun -S
- 在主应用中注册微应用
- 在微应用中导出相应的生命周期钩子并配置微应用的打包工具
微应用要做的具体事项:
- 新增
public-path.js文件,用于修改运行时的publicPath - 微应用建议使用
history模式的路由,需要设置路由base,值和它的activeRule是一样的 - 在入口文件最顶部引入
public-path.js,修改并导出三个生命周期函数。 - 修改
webpack打包,允许开发环境跨域和umd打包。
- 新增
EMP(构建时确定依赖关系)
基于 webpack5 module federation 的微前端方案。
特点
- webpack 联邦编译可以保证所有子应用依赖解耦;
- 应用间
去中心化的调用、共享模块、速度提升; - 模块远程 ts 支持;
不足
- 对 webpack 强依赖,老旧项目不友好;
- 没有有效的 css 沙箱和 js 沙箱,需要靠用户自觉;
- 子应用保活、多应用激活无法实现;
- 主、子应用的路由可能发生冲突;
- 如果不同应用之间是不同的框架,需要借助第三方去解决
- 本地开发需要开启多个端口的服务,比较麻烦
Module Federation
不同应用分为host和remote
- host:引用了其他应用的应用
- remote:被其他应用所使用的应用
提供了能在当前应用中远程加载其它服务器上应用的能力,基于此可以实现一个去中心化的应用部署群,每个应用是单独部署在各自的服务器,每个应用都可以引用其它应用,也能被其它应用所引用。通过new ModuleFederationPlugin配置被远程引用时的路径(exposes)、远程引用的应用、与其它应用之间可以共享的第三方依赖(shared)
// emp-config.js文件
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// 其他webpack配置...
plugins: [
new ModuleFederationPlugin({
name: 'empBase',
library: { type: 'var', name: 'empBase' },
filename: 'emp.js',
remotes: {
app_two: "app_two_remote",
app_three: "app_three_remote"
},
exposes: {
'./Component1': 'src/components/Component1',
'./Component2': 'src/components/Component2',
},
shared: ["react", "react-dom","react-router-dom"]
})
]
}
| 字段名 | 类型 | 含义 |
|---|---|---|
| name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose} |
| library | object | 声明全局变量的方式,name为umd的name |
| filename | string | 构建输出的文件名 |
| remotes | object | 远程引用的应用名及其别名的映射,使用时以key值作为name |
| exposes | object | 被远程引用时可暴露的资源路径及其别名 |
| shared | object | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |
Web Components
特点:
- 浏览器的原生组件,可以创建可重用的定制元素,不必担心代码冲突。
- 技术栈无关,Web Components是浏览器原生组件,即可以在任何JS框架中使用。
- 独立开发:使用Web Components开发的应用无需依赖其它应用
- 应用间隔离:Shadow DOM的特性,使得引入的微应用间可以达到相互隔离的效果 不足: Web Components也有浏览器兼容性的限制
主要由3项技术组成
Custom elements(自定义元素):一组JavaScript API,允许定义custom elements及其行为,然后可以在页面中使用。
shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其它部分发生冲突
TML templates(HTML模板):使用和元素可以编写不在呈现页面中显示的标记模板,然后它们可以作为自定义元素结构的基础被多次重用。
micro-app
micro-app 是基于 webcomponent + qiankun sandbox 的微前端方案。
特点
- 使用 webcomponet 加载子应用相比 single-spa 这种注册监听方案更加优雅;
- 复用经过大量项目验证过 qiankun 的沙箱机制也使得框架更加可靠;
- 组件式的 api 更加符合使用习惯,支持子应用保活;
- 降低子应用改造的成本,提供静态资源预加载能力;
不足
- 接入成本较 qiankun 有所降低,但是路由依然存在依赖;
- 多应用激活后无法保持各子应用的路由状态,刷新后全部丢失;
- css 沙箱依然无法绝对的隔离,js 沙箱做全局变量查找缓存,性能有所优化;
- 支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码没办法做沙箱隔离;
- 对于不支持 webcompnent 的浏览器没有做降级处理;
概念解释
子应用保活
当子应用设置为保活模式,切换子应用后仍然可以保持子应用的状态和路由不会丢失。
子应用嵌套
嵌套的应用和正常应用一致,支持预加载、保活、同步、通信等能力,需要注意的是内嵌的子应用 name 也需要保持唯一性,否则将复用之前渲染出来的应用
多应用激活
一个页面同时激活多个子应用并且保持这些子应用路由同步的能力。
应用共享
一个微前端系统可能同时运行多个子应用,不同子应用之间可能存在相同的包依赖,那么这个依赖就会在不同子应用中重复打包、重复执行造成性能和内存的浪费。