主應(yīng)用
1就漾、安裝 qiankun
npm i qiankun -S
2、項(xiàng)目中 src 文件夾下新建 qiankun 文件夾以及 qiankun.js 文件
import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';
/**
* 在主應(yīng)用中注冊(cè)微應(yīng)用
*/
registerMicroApps([
{
name: 'zsyftxglpt-mh', // 微應(yīng)用名稱 - 具有唯一性
entry: 'http://192.168.20.4:1973', // 微應(yīng)用入口 - 通過該地址加載微應(yīng)用
container: '#microApp__zsyftxglptMh', // 微應(yīng)用掛載節(jié)點(diǎn) - 微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
activeRule: '/zsyftxglptMh', // 微應(yīng)用觸發(fā)的路由規(guī)則 - 觸發(fā)路由規(guī)則后將加載該微應(yīng)用
props: {} // 主應(yīng)用需要傳遞給微應(yīng)用的數(shù)據(jù)
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#microApp__yourContainer',
activeRule: '/yourActiveRule',
},
]);
addGlobalUncaughtErrorHandler(event => {
const { msg } = event;
if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
console.log('加載失敗');
}
});
// 啟動(dòng) qiankun
// start(); // 推薦在頁面中開啟 qiankun
3痕慢、在 main.js 中 引入 qiankun.js
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
/**
* 引入 qiankun
*/
import './qiankun/qiankun.js';
createApp(App).use(router).mount('#app');
4尚揣、主應(yīng)用路由文件修改
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "home" */ '@/views/login/login.vue'),
meta: {
title: '登陸'
}
},
{
path: '/home',
component: () => import(/* webpackChunkName: "home" */ '@/views/home/home.vue'),
children: [
{
path: '/zsyftxglptMh/:page*', // 如果微應(yīng)用單獨(dú)作為主應(yīng)用的一個(gè)路由使用,vue-router 4.0 版本以上寫法
// path: '/zsyftxglptMh/*', // vue-router 3.0 版本寫法
name: 'zsyftxglptMh',
component: () => import(/* webpackChunkName: "home" */ '@/views/zsyftxglptMh/zsyftxglptMh.vue'),
meta: {
title: '知識(shí)門戶'
}
},
]
}
]
});
router.afterEach((to, from) => {
document.title = '研發(fā)體系管理平臺(tái) - ' + to.meta.title;
});
export default router;
4掖举、在頁面中啟動(dòng) qiankun
<template>
<div id="zsyftxglptMh" class="zsyftxglptMh"></div>
</template>
<script setup>
import { onMounted } from 'vue';
import { start } from 'qiankun';
onMounted(() => {
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start(); // 啟動(dòng) qiankun
}
});
</script>
<style src="./knowledgePortal.scss" lang="scss" scoped></style>
<style>
#__qiankun_microapp_wrapper_for_zsyftxglpt_mh__ {
height: 100%;
}
</style>
微應(yīng)用
1快骗、在 src 目錄新增 public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2、入口文件 main.js 修改,為了避免根 id #app 與其他的 DOM 沖突方篮,需要限制查找范圍
import './public-path'; // public-path 必須第一行就引入
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
// 重新包裝 render 方法
let instance = null;
function render(props = {}) {
const { container } = props;
// 如果是作為微應(yīng)用思灌,則渲染到主應(yīng)用中 qiankun 節(jié)點(diǎn)下的 #app 中, 如果作為獨(dú)立運(yùn)行時(shí),則渲染到 #app 節(jié)點(diǎn)中
const renderContainer = container ? container.querySelector('#app') : '#app';
instance = new Vue({
router,
store,
render: h => h(App),
}).$mount(renderContainer);
}
// 獨(dú)立運(yùn)行時(shí)
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 導(dǎo)出主應(yīng)用識(shí)別所需的三個(gè)必要的生命周期鉤子
/**
* bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次恭取,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子泰偿,不會(huì)再重復(fù)觸發(fā) bootstrap。
* 通常我們可以在這里做一些全局變量的初始化蜈垮,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等耗跛。
*/
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
/**
* 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
*/
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props); // 作為微應(yīng)用渲染時(shí)將主應(yīng)用傳入的配置作為參數(shù)去渲染
}
/**
* 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法攒发,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
*/
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
// router = null;
}
/**
* 可選生命周期鉤子调塌,僅使用 loadMicroApp 方式加載微應(yīng)用時(shí)生效
*/
export async function update(props) {
console.log('update props', props);
}
3、修改路由文件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '../views/HomeView.vue')
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/zsyftxglptMh/' : '/', // 如果作為 qiankun 的微應(yīng)用則使用主應(yīng)用中配置的路由前綴惠猿,如果是獨(dú)立運(yùn)行則使用根目錄
routes,
});
export default router
4羔砾、修改 vue.config.js
const packageName = require('./package.json').name;
module.exports = defineConfig({
configureWebpack: config => {
config.output.library = `${packageName}-[name]`;
config.output.libraryTarget = 'umd'; // 把微應(yīng)用打包成 umd 庫格式
config.output.jsonpFunction = `webpackJsonp_${packageName}`; // webpack 4 使用此選項(xiàng),如果是 webpack 5 使用下邊的選項(xiàng)偶妖,兩者選擇其一
// config.output.chunkLoadingGlobal = `webpackJsonp_${packageName}`;
},
devServer: {
port: 7100,
headers: {
'Access-Control-Allow-Origin': '*'
},
}
})
常見問題
1姜凄、主應(yīng)用 與 微應(yīng)用之間的服務(wù)代理
主應(yīng)用需要配置 與 微應(yīng)用一樣的服務(wù)代理
2、跳轉(zhuǎn)
// 主應(yīng)用 跳轉(zhuǎn) 微應(yīng)用
router.push()
// 微應(yīng)用 跳轉(zhuǎn) 主應(yīng)用
window.history.pushState(null, null, '/zsyftxglptMh/home');
3趾访、跳轉(zhuǎn)微應(yīng)用時(shí)遇到的問題
主應(yīng)用跳轉(zhuǎn)微應(yīng)用頁面為空态秧,如果控制臺(tái)提示 Uncaught Error: application 'xxxx' died in status LOADING_SOURCE_CODE: [qiankun]: Target container with #xxxx not existed while xxxx loading!
,這個(gè)是情況的出現(xiàn)是主應(yīng)用注冊(cè)微應(yīng)用時(shí) container 參數(shù)配置的元素不存在導(dǎo)致
registerMicroApps([
{
name: 'zsyftxglpt-mh', // 微應(yīng)用名稱 - 具有唯一性
entry: 'http://192.168.20.4:1973', // 微應(yīng)用入口 - 通過該地址加載微應(yīng)用
container: '#zsyftxglptMh', // 微應(yīng)用掛載節(jié)點(diǎn) - 微應(yīng)用加載完成后將掛載在該節(jié)點(diǎn)上
activeRule: '/zsyftxglptMh', // 微應(yīng)用觸發(fā)的路由規(guī)則 - 觸發(fā)路由規(guī)則后將加載該微應(yīng)用
props: {} // 主應(yīng)用需要傳遞給微應(yīng)用的數(shù)據(jù)
}
]);
// 需要在 public 下的 index.html 中 或者 APP.vue 或者 嵌套路由 <router-view></router-view> 同級(jí)添加 `zsyftxglptMh` 元素
<div id="zsyftxglptMh"></div>
4扼鞋、主應(yīng)用與微應(yīng)用之間互相通信
主應(yīng)用向微應(yīng)用傳值
主應(yīng)用使用 qiankun 內(nèi)置函數(shù) initGlobalState申鱼,設(shè)置全局變量,通過 setGlobalState 向微應(yīng)用傳遞 lang 參數(shù)的 CN 值
import { initGlobalState } from 'qiankun'
data () {
return {
globalState: null
}
},
mounted () {
console.log('Main App Home mounted')
this.globalState = initGlobalState({
lang: ''
})
},
postMsgToVueAPP () {
this.globalState.setGlobalState({
lang: 'CN'
})
}
子應(yīng)用在 mount 函數(shù)中接受 props 參數(shù),通過 onGlobalStateChange 函數(shù)監(jiān)聽主應(yīng)用傳遞過來的值
export async function mount(props) {
// 使用 Vue 原型屬性
Vue.prototype.parentStore = props
props.onGlobalStateChange((state) => {
console.log('子應(yīng)用接受的主應(yīng)用數(shù)據(jù)')
console.log(state)
}, true);
render(props);
}
微應(yīng)用向主應(yīng)用傳值
主應(yīng)用設(shè)置 onGlobalStateChange 監(jiān)聽全局?jǐn)?shù)據(jù)狀態(tài)變化
import { initGlobalState } from 'qiankun'
data () {
return {
globalState: null
}
},
mounted () {
console.log('Main App Home mounted')
this.globalState = initGlobalState({
lang: ''
})
this.globalState.onGlobalStateChange(state => {
// 監(jiān)聽全局狀態(tài)云头,子應(yīng)用更新主應(yīng)用數(shù)據(jù)后觸發(fā)
console.log(state)
})
},
子應(yīng)用使用 setGlobalState 更新全局狀態(tài)數(shù)據(jù)
// parentStore 為 `mount` 中設(shè)置到 Vue 原型屬性中的值
this.parentStore.setGlobalState({
lang: 'ZN'
})
5捐友、主應(yīng)用與微應(yīng)用的樣式隔離
elementui 修改前綴
參考地址
https://blog.csdn.net/weixin_44008717/article/details/121617721
使用 change-prefix-loader [https://www.npmjs.com/package/change-prefix-loader] 隔離 js
使用 postcss-change-css-prefix [https://www.npmjs.com/package/postcss-change-css-prefix] 隔離 css
element plus 修改前綴
參考地址
https://element-plus.gitee.io/zh-CN/guide/namespace.html
<!-- App.vue -->
<template>
<el-config-provider namespace="ep">
<!-- ... -->
</el-config-provider>
</template>
// src/assets/css/index.scss
@forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
$namespace: 'ep'
);
@use "element-plus/theme-chalk/src/index.scss" as *;
// vue.config.js
module.exports = defineConfig({
chainWebpack: config => {
/* 自定義配置路徑別名 */
config.resolve.alias.set('@', resolve('src'))
},
css: {
loaderOptions: {
scss: {
additionalData: `@use "~@/assets/css/index.scss" as *;`
}
}
}
})