微前端之背景与实践
微前端最早于2016年在 Micro-Frontends 被提出,并建⽴了早期的微前端模型。微前端的命名和能⼒和微服务有类似之处,微服务与微前端,都是希望将某个单⼀的单体应⽤,转化为多个可以独⽴运⾏、独⽴开发、独⽴部署、独⽴维护的服务或者应⽤的聚合,从⽽满⾜业务快速变化及分布式多团队并⾏开发的需求。如康威定律(Conway’s Law)所⾔,设计系统的组织,其产⽣的设计和架构等价于组织间的沟通结构;微服务与微前端不仅仅是技术架构的变化,还包含了组织⽅式、沟通⽅式的变化。微服务与微前端原理和软件⼯程,⾯向对象设计中的原理同样相通,都是遵循单⼀职责(Single Responsibility)、关注分离(Separation of Concerns)、模块化(Modularity)与分⽽治之(Divide & Conquer)等基本的原则。
课程目标
- 微前端如何解决业务场景的痛点,以
qiankun
为例接⼊微前端。 - 微前端的核⼼实现原理,从0到1实现简单的微前端框架。
知识要点
微前端的背景
微前端是什么
⼀种类似于微服务的架构,是⼀种由独⽴交付的多个前端应⽤组成整体的架构⻛格,将前端应⽤分解成⼀些更⼩、更简单的能够独⽴开发、测试、部署的应⽤,⽽在⽤户看来仍然是内聚的单个产品。
现代web应⽤⾯临的问题
DX(developer experience)
- 多个系统在⼀个仓库应⽤中,不同⼦应⽤独⽴SPA模式
- 系统分为多个仓库,独⽴上线部署,采⽤MPA模式
UX(user experience)
- 性能体验
- ⻚⾯跳转和⽤户体验问题
解决⽅案-微前端
微前端的意义
微前端的主要特点
- 低耦合:当下前端领域,单⻚⾯应⽤(SPA)是⾮常流⾏的项⽬形态之⼀,⽽随着时间的推移以及应⽤功能的丰富,单⻚应⽤变得不再单⼀⽽是越来越庞⼤也越来越难以维护,往往是改⼀处⽽动全身,由此带来的发版成本也越来越⾼。微前端的意义就是将这些庞⼤应⽤进⾏拆分,并随之解耦,每个部分可以单独进⾏维护和部署,提升效率。
- 不限技术栈:在不少的业务中,或多或少会存在⼀些历史项⽬,这些项⽬⼤多以采⽤⽼框架类似(Backbone.js,Angular.js 1)的B端管理系统为主,介于⽇常运营,这些系统需要结合到新框架中来使⽤还不能抛弃,对此我们也没有理由浪费时间和精⼒重写旧的逻辑。⽽微前端可以将这些系统进⾏整合,在基本不修改来逻辑的同时来同时兼容新⽼两套系统并⾏运⾏。
微前端解决的问题
- 业务领域的代码库不够独立和高度可重用
- 相同的产品功能由多个团队开发/产品功能难以保持统一
- 新的产品理念无法在不同的应用中快速复用/实现
- 快速迭代新子业务/干净移除将被淘汰的子业务
- 提升构建效率
- 改善交付效率
- 架构渐进升级
- 子团队的独立性
微前端的价值
微前端的方案
微前端应该具备哪些能力
⼀些可以实现微前端的⽅案
使⽤ HTTP 服务器的路由来重定向多个应⽤
在不同的框架之上设计通讯、加载机制,诸如 Single-SPA qiankun icestark
通过组合多个独⽴应⽤、组件来构建⼀个单体应⽤
- 微前端之构建时⽅案(Module Federation EMP)
iframe。使⽤ iframe 及⾃定义消息传递机制
- 使⽤纯 Web Components 构建应⽤
- 结合 Web Components 构建
业界主流的微前端框架
- single-spa :社区公认的主流⽅案,可以基于它做⼆次开发
- qiankun :基于 single-spa 封装,增加 umi 特⾊,增加沙箱机制(JS、ShadowDOM 等)
- icestark :类似于 single-spa 实现,React 技术栈友好,阿⾥的另⼀个轮⼦
基于 qiankun 的微前端实战
创建主应用基座
基坐主要实现微应⽤框架的初始化和注册等,通常没有具体的应⽤业务逻辑在⾥边
创建主应用
- 使用 React 创建主应用
1 | npx create-react-app main |
- 使用 Vue 创建主应用
1 | npm install -g @vue/cli |
创建微应用容器
在主应⽤中创建微应⽤的承载容器,这个容器规定了微应⽤的显示区域,微应⽤将在该容器内渲染并显示。
注册微应用
构建好了主框架后,需要使⽤ 的 registerMicroApps ⽅法注册微应⽤
1 | // index.js |
启动微应用
1 | // index.js |
接入微应用
qiankun 内部通过 import-entry-html 加载微应⽤,要求微应⽤需要导出⽣命周期钩⼦函数
- 导出相应的生命周期钩子
微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('react app bootstraped');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(
props.container ? props.container.querySelector('#root') : document.getElementById('root'),
);
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
} - 配置微应用的打包工具
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置:1
2
3
4
5
6
7
8
9const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
接入 Vue 微应用
接入 React 微应用
补充知识点
DIY 微前端实现核心能力
应用注册
- registerMicroApps
监听路由变化
- history
- hashHistory
生命周期
- 主应用
- beforeLoad:挂载子应用前
- mounted:挂载子应用后
- unmounted:卸载子应用
- 子应用
- bootstrap:首次应用加载触发,常用于配置子应用全局信息
- mount:应用挂载时触发,常用于渲染子应用
- unmount:用于卸载时触发,常用于销毁子应用
路由劫持
- 路由变化时匹配子应用
- 执行子应用生命周期
- 加载子应用
资源加载
- 加载样式表
- 提取 js 代码
- 执行 js 渲染
JS 沙箱
1 | export class ProxySandbox { |