之前项目的后台管理前端系统是用vue2写的,由于当时是一个团队多个业务聚集在一起,就都放在了一个前端项目里管理。
通过顶部的tab进行切换
但是最近由于团队切分,以及业务增多,导致开发,测试,发布上线等都很麻烦,于是就想着用qiankun微前端将各个的业务切分出去。
废话不多说,开干。
首先、主应用引入qiankun插件
npm i qiankun -S
然后按照官网的文档是需要在入口文件注册微应用的
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();
但是,由于我这边的子应用路由是动态路由,放在了router-view'里,如果在主应用注册,就会找不到子应用的容器。所以我这边把注册放到了layout组件里面。
然后在mounted生命周期中注册子应用。这样,每次刷新页面或者顶部tab切换路由都能找到子应用容器。
mounted() {
let urpt = document.getElementById('urpt')
this.$nextTick(() => {
console.log('子应用找到了urpt', urpt)
if (urpt) {
// 注册子应用
registerMicroApps([
{
name: 'traffic.urpt.admin.web',
entry: process.env.NODE_ENV === 'production' ? `//${window.location.host}/urpt/` : '//localhost:8080/urpt/',
container: '#urpt',
activeRule: '/saas/platform/urpt'
}
])
// 启动
start()
}
})
},
注册完子应用后,router.js添加子应用路由
export default [
{
path: '/platform',
icon: 'key',
name: 'platform',
title: '城乡客运管理',
access: 0,
component: Main,
tag: 'platform',
children:[
{
path: 'urpt',
title: '城乡客运',
name: 'urpt'
},
]
}
]
这样,主应用基座就基本上ok了。
接下来,开始创建子应用项目,在入口文件main.js导出微应用生命周期。
// main.js
import './public-path'
import { lifeCycle, render } from './life-cycle'
/**
* @name 统一注册插件、样式
*/
import './install'
/**
* @name 导出微应用生命周期
*/
const { bootstrap, mount, unmount } = lifeCycle()
export { bootstrap, mount, unmount }
/**
* @name 单独环境直接实例化vue
*/
const __qiankun__ = window.__POWERED_BY_QIANKUN__
__qiankun__ || render()
由于子应用是用vue3开发,life-cycle里面添加了element-plus库以及pinia状态管理插件等。
// life-cycle.js
import { createApp } from 'vue'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import '@/styles/index.less'
import ElementPlus from 'element-plus' // element-ui for vue3
import 'element-plus/dist/index.css' // element-ui css
import zhCn from 'element-plus/es/locale/lang/zh-cn'
// import elementIcon from "@/plugins/svgicon";
import * as Icons from '@element-plus/icons-vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import App from './App.vue'
import { createPinia } from 'pinia'
import { mainStore } from './store/store.js'
import selfRoutes from './router/routes'
import { getUserInfo } from '@/api/layoutAPI'
/**
* @name 导入官方通信方法
*/
import appStore from './utils/app-store'
const __qiankun__ = window.__POWERED_BY_QIANKUN__
const pinia = createPinia()
let router = null
let instance = null
/**
* @name 导出生命周期函数
*/
const lifeCycle = () => {
return {
/**
* @name 微应用初始化
* @param {Object} props 主应用下发的props
* @description bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发
* @description 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等
*/
async bootstrap(props) {
console.log('props:', props)
/* props.emits.forEach(i => {
Vue.prototype[`$${i.name}`] = i;
}); */
},
/**
* @name 实例化微应用
* @param {Object} props 主应用下发的props
* @description 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
async mount(props) {
console.log(props)
// 注册应用间通信
appStore(props)
// 注册微应用实例化函数
render(props)
},
/**
* @name 微应用卸载/切出
*/
async unmount() {
instance.$destroy ? instance.$destroy() : ''
instance = null
router = null
},
/**
* @name 手动加载微应用触发的生命周期
* @param {Object} props 主应用下发的props
* @description 可选生命周期钩子,仅使用 loadMicroApp 方式手动加载微应用时生效
*/
async update(props) {
console.log('update props', props)
},
}
}
/**
* @name 子应用实例化函数
* @param {Object} props param0 qiankun将用户添加信息和自带信息整合,通过props传给子应用
* @description {Array} routes 主应用请求获取注册表后,从服务端拿到路由数据
* @description {String} 子应用路由前缀 主应用请求获取注册表后,从服务端拿到路由数据
*/
const render = ({ container } = {}) => {
router = createRouter({
history: createWebHashHistory('/merchant'),
routes: __qiankun__ ? selfRoutes : selfRoutes,
})
let app = createApp(App)
// 全局注册图标
for (let i in Icons) {
app.component(i, Icons[i])
}
instance = app
.use(ElementPlus, {
size: 'default',
locale: zhCn,
})
.use(router)
.use(pinia)
.mount(container ? container.querySelector('#app') : '#app')
const store = mainStore()
}
export { lifeCycle, render, router }
注意:main.js里面引入了public-path文件,是为了解决主服务器加载js,css,图片等资源路径404的问题。
// public-path.js
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
// 解决主服务加载js.css 图片等资源路径404问题
最后在router路由页面配置一下路由就可以了
import AppMain from '../views/layout/index.vue'
const routes = [
{
path: '/',
redirect: '/merchant',
},
{
path: '/merchant',
name: 'merchantApp',
component: AppMain,
children: [
{
path: '/merchant',
name: 'merchant',
component: () => import('../views/merchant/index.vue'),
}
]
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue'),
},
]
export default routes