巨石应用,往往技术古老、逻辑耦合、性能较差、体验欠佳,再加上前期对系统定位不清晰,积年累月,包袱越来越重,一个很小的改动就可能牵一发而动全身,让人望而却步。而近期的开发任务都是在"巨石应用"上新增需求,然后就探到宝藏: Fes.js 微前端.
什么是微前端?为什么使用微前端?这些问题有很多文章都可深扒,这里就不赘述啦~本文我们主要扒一扒 Fes.js 微前端。先简单唠下 Fes.js 和微前端的关系: Fes.js 是一个前端应用解决方案,以 Vue 3.0 和路由为基础,覆盖编译时、运行时生命周期完善的的插件体系,支持各种功能扩展和业务需求;Fes.js 通过 @fesjs/plugin-qiankun 插件扩展了微前端,除了常规微前端功能,还提供了 复杂场景加载、样式隔离、最佳通讯 等进阶功能。
近期陆陆续续基于 Fes.js 微前端实现了几个巨石应用的新增需求,总结一下这么去玩的流程和推荐方案~对于老项目使用 Fes.js 微前端如何迁移,可以扒一扒 这篇 文章。
1.如何规划微应用、主应用
通常拿到新需求,做需求拆分时,我们就会根据需求的独立性、可复用性、使用场景和项目现技术栈几个角度考虑是否可以用、需要用微前端实现。那对于使用微前端实现的项目,一般规划如下:
代码库及结构
主应用、微应用各自独立的代码库。通常,微应用代码库建议以主应用代码库-micro命名。
- 简单微应用:微应用代码库内部的 src/pages/ 下每个文件为一个独立微应用。
- 复杂/通用微应用:如果一个微应用需要被多个主应用引用,且多个主应用之间数据处理差异较大,一个更好的处理方式是在 src/pages/ 下根据主应用创建不同的微应用路由入口,并在路由入口处抹平数据差异,最后统一在 src/components/ 通用组件处理相同逻辑。
技术栈
微前端的核心是技术栈无关,微应用、主应用使用自己的技术栈独立开发。
- 微应用: Fes.js、Vue3、UI 组件库自选(Fes UI 敬请期待中~)
- 主应用: 老项目原有技术栈无需变更,新项目推荐 Fes.js 。
微应用接入主应用
- 由于技术栈无关,所以微应用接入是一个协议接入。微应用不需要额外安装任何其他依赖,只需要在自己的入口 js 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。
主应用加载微应用
- 基于 Fes.js 的主应用: 按照 @fesjs/plugin-qiankun 指引配置加载微应用即可。
- 其他技术栈的主应用: 在主应用安装 qiankun 插件,通过 loadMicroApp() API 加载微应用。
打包、部署、更新
- 打包: 主应用、微应用各自独立打包,完全解耦。
- 部署: 主应用、微应用各自独立部署,互不影响,更有利于独立迭代。
- 更新: 主应用、微应用各自独立更新。对于仅涉及微应用的更新,微应用部署完成后(所有引用它的)主应用自动完成同步更新。
2.微应用相关
基于 Fes.js 如何开发微应用,官方文档和上面 ???? 文章都有写,十分清楚啦~这里着重讨论一下关于微应用的样式隔离,这趴我们内部项目是踩了一些坑的,也讨论过最佳实践,这里完整总结一下。
微应用样式隔离最佳实践
1. qiankun Shadow DOM:
qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响。Shadow DOM 大部分情况下都需要主应用做一些适配后才能正常在 Shadow DOM 中运行起来。需要适配的点跟项目相关联,很具有未知性,成本较大!
2. qiankun experimentalStyleIsolation: true
当 experimentalStyleIsolation 设置为 true 时,编译后会给微应用的 最外层容器 添加一个特殊选择器,并将这个特殊选择器添加到所有样式规则上,来限定微应用样式影响范围。无法解决“主应用与微应用有同名选择器”情况下的样式污染。
3. BEM
通过一些 约定 (如统一加微应用前缀) 来避免冲突,比较有效的方案。强依赖约定。
4. scoped CSS【推荐】
scoped 的原理是生成一个 data-v-[hash] 形式的data属性选择器作为唯一标识,在编译后生成的每句 css 选择器的末尾加一个当前元素的"data属性选择器"来私有化样式。这样局部样式可以做到极大程度上避免样式冲突,但也无法解决方案2的问题。
5. css Module【推荐】
css Module 会根据 文件名+类名+hash 生成一个唯一标识,然后直接替换掉原类名,比 scoped 更为彻底。这样只要不直接通过标签选择器去修改样式,基本可以做到完全避免冲突。当前 css module 已经是非常成熟一种做法,通过在构建 工具 里加一些 css 预处理器即可实现。如果是基于 Fes.js 的微应用,则更无需关心任何额外配置,直接在代码里面使用即可,其他工序都暗戳戳帮你搞定啦~
3.主应用相关
主应用相关的主要讲一下 如何加载、如何通讯 两个方面。
主应用如何加载微应用?
在主应用加载微应用,在主应用封装一个 microCommon 组件, 来统一处理加载、通讯、微应用 unmount 等逻辑,然后在主应用按需引入 microCommon。以一个基于 vue2 的主应用为例,通过上面【主应用加载微应用】第2种类型接入微应用。
1. 实现 microCommon.vue 组件
<template>
<div></div>
</template>
复制代码
import { loadMicroApp } from 'qiankun';
export default {
props: {
microName: String,
microContainer: String,
microEntry: String,
microPropsUrl: String
},
data() {
return {
microApp: null
};
},
mounted() {
this.microApp = loadMicroApp({
name: this.microName,
container: `#${this.microContainer}`,
entry: `${this.microEntry}`,
props: {
url: this.microPropsUrl
}
});
},
async beforeDestroy() {
if (this.microApp) {
const status = this.microApp.getStatus();
if (status === 'MOUNTED') await this.microApp.unmount();
}
return Promise.resolve();
}
};
复制代码
2. 主应用引用 microCommon 组件:
<template>
<div id="materialManage_materialEditing">
<micro-common
:microName="'materialManageEditing'"
:microContainer="'materialManage_materialEditing'"
:microEntry="'/s/web-mas-micro/'"
:microPropsUrl="'/materialManage/materialEditing'"
></micro-common>
</div>
</template>
复制代码
import MicroCommon from '@/components/microCommon';
export default {
components: {
MicroCommon
}
};
复制代码
主应用如何与微应用通讯?
1. 基于 props 通讯
在 qiankuan loadMicroApp API 有详细关于 props 传参的示例,并结合 initGlobalState API 实现传递数据的更新和管理。除了参数,方法也可以通过 props 传入微应用使用。
2. Fes.js 最佳实践:基于 useModel 通讯
基于该通讯方式,子应用获取 props 的方法是相同的,子应用中会自动生成一个全局名为 qiankunStateFromMain 的 model,可以在任意组件中获取主应用传递的 props 的值。主应用的传递 props 方式有所差异:
- 基于 Fes.js 的主应用:按照 @fesjs/plugin-qiankun 文档指引区分不同微应用引入方式传递 props。
- 其他技术栈的主应用:通过 loadMicroApp API 传递 props。
作者:tian_xuan
fes.js 社区微信:geniusWanc