1. 微前端架构目录结构
1 | ├── common //公共模块 |
2. 基座配置
子应用配置文件
1 | const microApps = [ |
基座配置
1 | import Vue from 'vue'; |
3. 子应用配置
1 | import './public-path' // 注意需要引入public-path |
4. 全局状态管理
通过initGlobalState, onGlobalStateChange, setGlobalState实现主应用的全局状态管理,然后默认会通过props
将通信方法传递给子应用:
1 | // 基座 main/src/main.js |
5. 子应用独立运行
基座下发的数据独立存储
子应用在mount
声明周期可以获取到最新的主应用下发的数据,然后将这份数据注册到一个名为global
的vuex module中,子应用通过global module的action动作进行数据的更新,更新的同时自动同步回父应用。因此,对子应用来说,它不用知道自己是一个qiankun子应用还是一个独立应用,它只是有一个名为
global
的module,它可通过action更新数据,且不再需要关心是否要同步到父应用(同步的动作会封装在方法内部,调用者不需关心),这也是为后面支持子应用独立启动开发做准备。
基座不启动时,主应用的数据怎么获取,子应用登录状态、权限等怎么维护?
在基座运行时,登录态和用户信息是存放在基座上的,然后基座通过props下发给子应用。但如果基座不启动,只是子应用独立启动,子应用就没法通过props获取到所需的用户信息了。因此,解决办法只能是父子应用都得实现一套相同的登录逻辑。为了可复用,可以把登录逻辑封装在common中,然后在子应用独立运行的逻辑中添加登录相关的逻辑。
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// sub-vue/src/main.js
import { store as commonStore } from 'common'
import store from './store'
if (!window.__POWERED_BY_QIANKUN__) {
// 这里是子应用独立运行的环境,实现子应用的登录逻辑
// 独立运行时,也注册一个名为global的store module
commonStore.globalRegister(store)
// 模拟登录后,存储用户信息到global module
const userInfo = { name: '我是独立运行时名字叫张三' } // 假设登录后取到的用户信息
store.commit('global/setGlobalState', { user: userInfo })
render()
}
// ...
export async function mount (props) {
console.log('[vue] props from main framework', props)
commonStore.globalRegister(store, props)
render(props)
}
// ...
6. 子应用切换Loading
qiankun提供了loader方法可以获取到子应用的加载状态,在这个方法里改动基座App.vue里的loading状态;给子应用配置加上loader方法:
1 | // 定义loader方法,loading改变时,将变量赋值给App.vue的data中的isLoading |
7. 公共代码抽离
不可避免,有些方法或工具类是所有子应用都需要用到的,每个子应用都copy一份肯定是不好维护的,所以抽取公共代码到一处是必要的一步。
根目录下新建一个common
文件夹用于存放公共代码,如上面的多个vue子应用都可以共用的global-register.js
,或者是可复用的request.js
和sdk
之类的工具函数等。
公共代码抽取后,其他的应用如何使用呢? 可以让common发布为一个npm私包,npm私包有以下几种组织形式:
- npm指向本地file地址:
npm install file:../common
。直接在根目录新建一个common目录,然后npm直接依赖文件路径。 - npm指向私有git仓库:
npm install git+ssh://xxx-common.git
。 - 发布到npm私服。
8. js/css隔离
js
沙箱的原理是子项目加载之前,对 window
对象做一个快照,子项目卸载时恢复这个快照
给 body
、 document
等绑定的事件,请在 unmount
周期清除。js
沙箱只劫持了 window.addEventListener
,使用 document.body.addEventListener
或者 document.body.onClick
添加的事件并不会被沙箱移除,会对其他的页面产生影响,请在 unmount
周期清除
qiankun
的 css
沙箱的原理是重写 HTMLHeadElement.prototype.appendChild
事件,记录子项目运行时新增的 style/link
标签,卸载子项目时移除这些标签。
9. 子应用独立仓库后聚合管理
子应用独立git仓库后,可以做到独立启动独立开发了,这时候又会遇到问题:开发环境都是独立的,无法一览整个应用的全貌。
虽然开发时专注于某个子应用时更好,但总有需要整个项目跑起来的时候,比如当多个子应用需要互相依赖跳转时,所以还是要有一个整个项目对所有子应用git仓库的聚合管理才行,该聚合仓库要求做到能够一键install所有的依赖(包括子应用),一键启动整个项目。
这里主要考虑了三种方案:
- 使用
git submodule
。 - 使用
git subtree
。 - 单纯地将所有子仓库放到聚合目录下并
.gitignore
掉。 - 使用lerna管理。