前言
本文主要分析对比了 MicroApp
、Qiankun
两大微服务框架,从各方面需求特性上分析每个框架的优缺点,每个微服务特性的方案的技术选型。最后如何根据自己的业务需求,选择适合自己的微服务框架,以及如何实现自己的微服务框架。
- Micro App
- 阿里 zeroing.jd.com/micro-app/
- 基于 Web Component 原生组件进行渲染的微前端框架。
- Qiankun
- 蚂蚁金服 qiankun.umijs.org/zh
- 基于 single-spa,弥补了不足,现在主流的微前端方案。
特性1. 加载子应用机制
MicroApp
js
// router.js
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import MyPage from './my-page'
export default function AppRoute () {
return (
<BrowserRouter>
<Switch>
// 👇 非严格匹配,/my-page/* 都指向 MyPage 页面
<Route path='/my-page'>
<MyPage />
</Route>
</Switch>
</BrowserRouter>
)
}
// my-page.js
export function MyPage () {
return (
<div>
<h1>子应用</h1>
// name(必传):应用名称
// url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
// baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
<micro-app name='app1' url='http://localhost:3000/' baseroute='/my-page'></micro-app>
</div>
)
}
- 通过 micro-app Web Components 自定义组件,将子应用页面嵌入到对应dom中。
- 两种机制:
- 基于路由配置:先在主应用里自己定义路由(比如react router),然后在路由组件里使用 micro-app,子应用里可以再定义子路由。
- 一个页面多个子应用:一个页面下使用多个 micro-app组件,
- 渲染子应用机制:
- 渲染 micro-app Web Components 初始化
- 组件通过fetch url,获取子应用的html;
- 分解处理html文本,获取css和js的资源地址;
- 通过fetch获取子应用的静态资源,也就是js和css的内容文本;
- 将处理过的html放入webComponent容器中,head放到micro-app-head dom里,body放到micro-app-body dom里;
- 给css加上 scope 前缀,并append到micro-app-head中;(样式隔离)
- 执行沙箱机制,在沙箱中执行js代码;(JS沙箱隔离)
- 完成子应用的初始化;
Qiankun
js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
]);
start();
- 两种机制:
- 基于路由配置:基于 single-spa,通过注册微应用方式(registerMicroApps),把路由path跟子应用关联起来,跳转对应路由时,微应用就会被插入到指定的container(dom)中。同时依次调用微应用暴露出的生命周期钩子。微应用里可以再定义子路由。
- 手动加载微应用:也支持一个页面同时存在多个微应用(loadMicroApp),也就是不依赖路由。
- 渲染子应用机制:
- 原理总体跟MicroApp类似,只是实现机制不一样。
- 路由层监听主要还是基于 single-spa 实现的,包括生命周期,Qiankun只是额外实现了解析html,获取和增强生命周期,隔离JS等操作,跟MicroApp类似。
特性2. 技术栈
MicroApp
- 用Web Components我理解好处是它可以不依赖框架,不用每个框架都写个组件,用一个原生组件维护就行。
- 如果想定义路由,可以用React或Vue框架;或者应该也有方法不用纯js定义路由,不需要Webpack编译。
- 主子应用可以用不同的框架,或者无框架。
- 有Shadow DOM机制,但官方不建议用。
Qiankun
- 因为自身支持路由,主应用不用定义路由机制,更不依赖框架。
- 主子应用可以用不同的框架,或者无框架。
- 虽然没用Web Components,不过内部也有Shadow DOM机制,但官方不建议用。
特性3. 样式隔离
方案选型:
- Shadow DOM
- 实现:JS原生。
- 优点:原生隔离。
- 缺点 :无法引用外部样式,需要额外加处理逻辑实现;兼容不好,下面举例:
- 一些弹窗组件会把dom挂在到document.body上,会导致元素逃离了阴影边界,跑到了主应用里面,样式就丢了。
- React框架的事件代理问题。React 17已支持 github.com/facebook/re...
- BEM规范,给样式加前缀
- 实现:开发内部给子应用样式统一加前缀。
- 优点:无污染;可以引用或覆盖外部样式。
- 缺点:需要各应用team约定规范前缀,尽量安排专人控制好scope,不要乱用滥用。
- css scope,给子应用所有样式加一层选择器来隔离样式
- 实现:
- 通过fetch读取子应用css文件内容,批量加scope;(MicroApp、Qiankun)
- 或者通过自定义Webpack loader\plugin等方式,在编译层加scope;(一种想法,还未找到类似实现)
- 优点:无污染,可控高;可以引用或覆盖外部样式。
- 缺点:需要加比较复杂逻辑实现;同样有上面说的弹框dom挂载body样式丢失问题,需要common控件做适配。
- 实现:
- css modules
- 优点:class name编译哈希字符串,独立无污染。
- 缺点:需要Webpack等构建工具支持;无法被重写覆盖,比如子应用重写覆盖主应用样式。(建议主应用common或layout样式用class name,其它用css modules)
MicroApp
- 通过fetch请求子应用html里的内外联样式,批量添加前缀,再引用到主html里,从而实现css scope机制。
- 默认支持css scope,配合css modules。
- 支持配置开启Shadow DOM,开启后css scope失效,但官方不建议开启。
- 主应用样式默认全局,可以被子应用使用;子应用样式独立,只能自己使用。可以通过叹号魔法注释配置隔离。
官方:Shadow DOM具有更好的隔离性,但一些框架(如React)对Shadow DOM的兼容性不好,请谨慎使用。
Qiankun 有4种
- 动态切换样式表。
- css modules。
- 可选:严格样式隔离:Shadow DOM,同上会有兼容问题。
sandbox.strictStyleIsolation
- 可选:scoped css:目前该特性还处于实验阶段。
sandbox.experimentalStyleIsolation
特性4. JS沙箱隔离
方案选型:
- window快照:主要用于不支持Proxy的低版本浏览器;性能差;只支持单微服务模式,也就是一个页面只能存在一个子应用。
- 基于Proxy的沙箱:function + proxy + with
- iframe
MicroApp
- 基于Proxy的沙箱。
Qiankun
- 旧版本用的是快照机制,新版本是基于Proxy的沙箱。
特性5. 元素DOM隔离
一般只在一个页面有多个子应用情况下需要考虑,比如在两个应用里有重复的id或class的DOM,在其中一个子应用里用通过document api或jQuery根据id class获取dom时,要求只能获取当前应用下的DOM。
方案选型:
- Shadow DOM:缺点同上面的样式隔离。
- 重写子应用操作DOM方法:通过代理Document\Element原型链上的方法来实现把选择器的范围控制到当前子应用内,比如document.getElementById。
MicroApp
- 重写子应用操作DOM方法。
Qiankun
- 默认没有隔离。
- 可选:严格样式隔离:Shadow DOM。
MicroApp、Qiankun 对比
框架 | 加载子应用机制 | 技术栈 | 支持多应用 | 样式隔离 | JS沙箱隔离 | 元素DOM隔离 |
---|---|---|---|---|---|---|
MicroApp | single-spa 基于路由 | 多框架或无框架兼容 | 支持 | css scope + css modules | Proxy | 重写操作DOM方法 |
Qiankun | Web Components | 多框架或无框架兼容 | 支持 | 动态切换样式表 + css modules | Proxy | 不支持 |
还有其它特性这里就不分析了,比如 资源预加载
数据通信
这些这俩框架都支持。
自定义微服务框架的技术选型
如果不用第三方的框架,想自己实现一个微服务框架,应该怎么实现呢?
技术选型推荐:
加载子应用机制 技术栈
感觉两种框架基本原理都一样,只是实现方式不同,一个用Web Components,另一个用方法,直接参考选一个就行。
样式隔离
- Shadow DOM:有兼容问题,不建议。
- 约定规范前缀:如果有定义子应用全局样式需求,可以使用,采用约定规范前缀,尽量安排专人控制好scope,不要乱用滥用。
- css scope:需要写复杂逻辑,而且有些case需要做适配,不建议。
- css modules:有webpack构建的都能用,建议使用。
- 建议主应用common或layout样式用class name,方便子应用重写,其它用css modules
综合看,css modules是比较合适,也是成本最低的方案。如果更高级些,想兼容性高,可以支持css scope,参考MicroApp实现方式。
JS沙箱隔离
首选基于Proxy的沙箱机制。实现代码网上很多,或者直接用俩框架源码。
元素DOM隔离
建议不要用document api或jQuery获取DOM,所有业务都用ref方式获取DOM,或者个别可以严格控制DOM id和class。
或者参考MicroApp,重写子应用操作DOM方法。
问题
- 是否需要Web Components?
MicroApp用了WCs,但是它的好处是,无论主应用用的什么框架,React还是Vue,都可以直接在jsx\template里写<micro-app
,不用MicroApp自己还得维护每个框架一个组件,还得根据不同框架打包,而且只有一个组件,它只是一个壳子,一个定义子应用位置的壳子。
如果想自己实现一个微服务框架产品,自己用,那就根据自己产品技术栈实现一个对应控件就行了。比如用React,就对应一个React组件,实现类似<micro-app
组件的效果。当然也可以用WCs定义,毕竟组件逻辑不太依赖框架,只是一个壳子。
- 公共控件是否需要Web Components?
- 如果子应用都是同一个框架,比如React,那没必要用WCs,直接定义一套React框架的公共组件库就行。
- 如果子应用可能是不同框架,比如有的React、有的Vue、有的是jQuery,那就需要定义一套WCs公共组件库。
总结
本文主要分析对比了 MicroApp
、Qiankun
两大微服务框架,从各方面需求特性上分析每个框架的优缺点,每个微服务特性的方案的技术选型。最后如何根据自己的业务需求,选择适合自己的微服务框架,以及如何实现自己的微服务框架。
另外,Webpack5 的 Module Federation 联邦模块 也可以用来当做另一种微前端的解决方案,但是使用场景跟上面说的框架不一样,之后文章也会对这个技术做介绍,看看它解决了微前端哪些问题,又能做到上面框架哪些实现不了的事儿。