微前端qiankun项目改造

微前端

最近项目需要解耦,比如系统设置相关的功能抽成了一个权限系统,而多个项目需要使用它,当其中一个项目中需要对功能进行升级,其它需要同样更新的项目也需要更新代码,产生的开销比较大。
如果将这些项目解耦,各自开发,在需要的时候插入到基座上,就不用担心项目之间的依赖关系,也不用担心项目之间的代码冲突。而微前端很好的解决了这个问题,微应用都放在各自的沙箱中。

基座项目改造

安装qiankun

npm i qiankun -S

微应用配置

zhNameentry_dev为我的自定义字段,用于开发需求,其它字段是qiankun框架必须的。

{
"APPCODE": "MicroBaseApp", // 动态菜单查询所需字段
"MICRO_APPS": [
{
"zhName": "应用1", // 微应用的中文名称
"name": "app1", // 微应用的名称,唯一标识
"entry": "http://xxx.com", // 微应用的入口
"entry_dev": "localhost:8080", // 微应用的开发环境入口
"container": "#appContainer", // 微应用挂载容器
"activeRule": "/app1" // 微应用的激活规则,涉及到路由分发
},
{
"zhName": "应用2",
"name": "app2",
"entry": "http://xxx.com",
"entry_dev": "localhost:8081",
"container": "#appContainer",
"activeRule": "/app2"
},
{
"zhName": "应用3",
"name": "app3",
"entry": "http://xxx.com",
"entry_dev": "localhost:8082",
"container": "#appContainer",
"activeRule": "/app3"
}
]
}

模板文件添加挂载容器

<div id="app"></div>
<!-- built files will be auto injected -->
<div id="appContainer"></div>

注册微应用并启动

import { registerMicroApps, start } from 'qiankun';
import axios from './axios';

axios.get(`${process.env.BASE_URL}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => {
// 读取配置
const cnotConfig = getConfig(res.data)
Vue.prototype.$config = cnotConfig
// 创建基座实例
const baseVue = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
// 基座加载完成执行挂载微应用
baseVue.$nextTick(() => {
// 微应用配置
const { MICRO_APPS: microApps } = cnotConfig
// 微应用入口
const entryAttr = process.env.NODE_ENV === 'development' ? 'entry_dev' : 'entry'
// 注册微应用
registerMicroApps(microApps.map((app) => ({
...app,
entry: app[entryAttr],
props: {
cnotConfig
}
})))

start({ opts: 'all', })
})
})

如果跨域,还需要配置devServer。

微应用项目改造

创建public-path.js,解决静态资源加载问题。

if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

main.js改造

import './public-path'
let instance = null
function render({container, parentStore} = {}){
instance = new Vue({
router,
store,
data(){
return {
parentStore
}
},
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}

// 独立运行时
// eslint-disable-next-line
if (!window.__POWERED_BY_QIANKUN__) {
axios.get(`${process.env.BASE_URL}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => {
Vue.prototype.$config = getConfig(res.data)
render()
})
}

// 作为微应用,暴露三个生命周期函数
export async function bootstrap () {
console.log('cnot vue app bootstraped')
}
export async function mount (props) {
// eslint-disable-next-line no-undef
axios.get(`${__webpack_public_path__}config/${process.env.VUE_APP_CONFIG}.json?t=${new Date().valueOf()}`).then(res => {
Vue.prototype.$config = getConfig(res.data)
// 动态菜单查询字段
// Vue.prototype.BaseAppCode = props?.anopConfig?.APPCODE ?? ''
render(props)
})
}
export async function unmount () {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
}

package.json需要添加name字段,需和基座name保持一致,配合webpack使用。

const { name } = require('./package')

output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`
}

同时,如果单页应用主容器使用了结构包装,需要通过判断是否显示导航以及面包屑等等。

<div class="fe-no-shrink" v-if="!isMicroApp">
<Navbar></Navbar>
<Breadcrumb></Breadcrumb>
</div>
<script>
import Navbar from '@/components/Navbar'
import Breadcrumb from '@/components/Breadcrumb'

export default {
name: 'Main',
components: {
Navbar,
Breadcrumb
},
data() {
return {
isMicroApp: !!window.__POWERED_BY_QIANKUN__
}
}
}
</script>

warning: 微应用和微应用之间样式会隔离,但是基座和微应用之间样式会互相影响,如果发生不在预期内的变化,建议加前缀进行人为隔离。

文章作者: Furo Yang
文章链接: http://furoteam.cn/2022/05/26/微前端qiankun项目改造/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 furoのBlog