一、业务背景
平台系统业务越来越庞大,十几个子应用模块,且部分业务需要同时在两个平台增加一样的模块,基于后续相同需求、分解业务模块、功能模块复用、降低维护成本以及技术同学对自身的成长需求,所以我们选定 乾坤(qiankun) 来解构应用。
二、乾坤(qiankun)
1、介绍
2、优缺点
- 优点:技术栈无关、代码库隔离、独立部署、应用通信
- 缺点:性能降低
三、实现
根据我们当前的项目业务,创建两个主应用基座App1、App2
微前端.png1、主应用
src根节点下添加qiankun文件夹
- qiankun/apps.js
{
name: 'handbill',
entry: 'http://localhost:8088',
container: '#handbillContainer',
activeRule: 'handbill'
}
]
module.exports = apps
- qianun/init.js
import {
registerMicroApps,
start
// setDefaultMountApp
} from 'qiankun'
import apps from '@/qiankun/apps'
export default function qiankunInit() {
// 注册微应用
registerMicroApps(
apps.map(app => {
return { ...app }
}),
{
beforeLoad: [
app => {
console.log('[初始化微应用 beforeLoad]', app.name)
}
],
beforeMount: [
app => {
console.log('[初始化微应用 LifeCycle]', app.name)
}
],
afterUnmount: [
app => {
console.log('[初始化微应用 afterUnmount]', app.name)
}
]
}
)
// 默认加载第一个微应用
// setDefaultMountApp(apps[0].activeRule)
// 启动 qiankun
start({
sandbox: { strictStyleIsolation: true } // 开启沙盒模式
})
}
在 src/layouts/App/index.vue 文件下 路由入口添加子应用容器ID: subapp-viewport
<transition name="fade-transform" mode="out-in">
<div id="subapp-viewport">
<keep-alive :include="include" :max="8">
<router-view />
</keep-alive>
</div>
</transition>
引入qiankun初始化
import qiankunInit from '@/qiankun/init'
mounted() {
qiankunInit()
}
2、微应用
微应用不需要下载qiankun依赖
src根节点下添加public-path文件
/* eslint-disable */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
/* eslint-disable */
src/main.js改造
import './public-path
import Vue from 'vue'
import App from './App.vue'
import router from '@/router'
import store from '@/store'
let instance = null
function render(props = {}) {
const { container } = props
instance = new Vue({
router,
store,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('[vue] vue app bootstraped')
}
export async function mount(props) {
console.log('[vue] props from main framework', props)
render(props)
}
export async function unmount() {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
router = null
}
vue.config.js改造
"Access-Control-Allow-Origin": "*"
configureWebpack.output = {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
}
const { name } = require('./package');
module.exports = {
publicPath: '/',
outputDir: 'dist',
devServer: {
headers: {
"Access-Control-Allow-Origin": "*"
},
port: 8088,
https: false,
hotOnly: true,
disableHostCheck: true,
open: true,
before: app => {}
},
productionSourceMap: false,
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
}
}
}
可以基于qiankun的全局变量判断是否微应用打开,隐藏菜单、头部、底部等公共组件。以达到可独立运行又可为微应用引入
/ 是否为微应用引入
get isMicroApp () {
return !!window.__POWERED_BY_QIANKUN__
}
3、应用通信
https://qiankun.umijs.org/zh/api#initglobalstatestate
主应用定义全局状态
全局状态只能在主应用中定义
import { initGlobalState } from 'qiankun'
// 全局状态数据
const state = {
loading: false, // 加载动画
}
// 初始化全局状态
const actions = initGlobalState(state)
// 监听全局状态
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
})
// 打开加载动画
state.loading = true
// 设置全局状态 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
actions.setGlobalState(state)
// 移除当前应用的状态监听,微应用 umount 时会默认调用
actions.offGlobalStateChange()
微应用定义全局状态
跟主应用使用规则基本一致,不需要定义全局状态
// src/main.js
const globalState = null
export async function mount(props) {
// 监听全局状态
props.onGlobalStateChange((state, prev) => {
globalState = state
console.log('微应用 state', state)
console.log('微应用 prev', prev)
})
// 关闭加载动画
globalState.loading = false
// 设置全局状态 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
props.setGlobalState(globalState)
render(props)
}
四、踩坑记录
探索qiankun的过程中碰到最多的就是找不到容器节点
not existed while xx mounting!
not existed while xxx loading!
查了两天发现是index.html引入的地图插件导致容器节点被重绘,导致qiankun的初始化过程中找不到容器,修改为依赖引入就好
网友评论