Vue 微前端搭建 qiankun

主應(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ù)代理
image.png

image.png
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 *;`
            }
        }
    }
})
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市溃槐,隨后出現(xiàn)的幾起案子匣砖,更是在濱河造成了極大的恐慌,老刑警劉巖竿痰,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脆粥,死亡現(xiàn)場(chǎng)離奇詭異砌溺,居然都是意外死亡影涉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門规伐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟹倾,“玉大人,你說我怎么就攤上這事∠侍模” “怎么了肌厨?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豁陆。 經(jīng)常有香客問我柑爸,道長,這世上最難降的妖魔是什么盒音? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任表鳍,我火速辦了婚禮,結(jié)果婚禮上祥诽,老公的妹妹穿的比我還像新娘譬圣。我一直安慰自己,他們只是感情好雄坪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布厘熟。 她就那樣靜靜地躺著,像睡著了一般维哈。 火紅的嫁衣襯著肌膚如雪绳姨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天阔挠,我揣著相機(jī)與錄音就缆,去河邊找鬼。 笑死谒亦,一個(gè)胖子當(dāng)著我的面吹牛竭宰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播份招,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼切揭,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了锁摔?” 一聲冷哼從身側(cè)響起廓旬,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谐腰,沒想到半個(gè)月后孕豹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡十气,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年励背,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砸西。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叶眉,死狀恐怖址儒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衅疙,我是刑警寧澤莲趣,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站饱溢,受9級(jí)特大地震影響喧伞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绩郎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一絮识、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗽上,春花似錦次舌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浅萧,卻和暖如春逐沙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洼畅。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國打工吩案, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帝簇。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓徘郭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丧肴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子残揉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容