記錄--微前端qiankun接入vue2&vue3專案

林恒發表於2024-11-15

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

官方文件

接入vue2專案和vue3專案

對應路由模式分別是hashhistory

主應用

主應用是基於vue3開發的一個入口網站,僅有登入以及門戶列表。

基於路由配置方式

透過將微應用關聯到一些 url 規則的方式,實現當瀏覽器 url 發生變化時,自動載入相應的微應用的功能。

  • 安裝qiankun

    npm i qiankun -S # 或者 yarn add qiankun

  • 在主應用中註冊微應用以及對應配置

    新建qiankun資料夾,包含一個config.jsindex.js

  • config下面存放微應用的配置資訊,程式碼如下:

    // qiankun/config.js
    // 這裡用函式的方式,方便初始化的時候可以設定一些預設引數傳進來,例如token等,
    // 用於下發給微應用
    export const getQiankunConfig = (props = {}) => {
      return {
        subApps: [
          {
            name: "xxx-wms", // 子應用名稱,建議跟package.json一致
            entry: "//192.168.1.40:8990/", // 子應用入口,本地環境下指定埠
            container: "#sub-container", // 掛載子應用的dom
            activeRule: "/xxx/wms", // 路由匹配規則
            props // 主應用與子應用通訊傳值
          },
          {
            name: "xxx-report", // 子應用名稱,跟package.json一致
            entry: "//192.168.1.40:9527/#/", // 子應用入口,本地環境下指定埠
            container: "#sub-container", // 掛載子應用的dom
            activeRule: "/xxx/report", // 路由匹配規則
            props // 主應用與子應用通訊傳值
          },
        ]
      };
    };
  • index放初始化函式以及一些其他需要配置和操作
// qiankun/index.js
import { registerMicroApps } from "qiankun";
import { getQiankunConfig } from "./configs";
import { getToken } from "@/plugins/cache";
import useUserStore from "@/store/modules/user";

// 設定初始需要傳遞的值
export const getState = () => {
  const { userInfo } = useUserStore();
  const state = {
    token: getToken(),
    accessUser: {
      loginName: userInfo.userName || "",
      realName: userInfo.userDisplayName || ""
    }
  };
  return state;
};

const { subApps } = getConfig(getState());
// 暴露註冊函式,裡面對應存放生命週期的鉤子,可以做需要的處理
export function registerApps() {
  try {
    registerMicroApps(subApps, {
      beforeLoad: [
        app => {
          console.log("before load", app);
        }
      ],
      beforeMount: [
        app => {
          console.log("before mount", app);
        }
      ],
      afterUnmount: [
        app => {
          console.log("before unmount", app);
        }
      ]
    });
  } catch (err) {
    console.log(err);
  }
}

新增容器,以及在路由表上增加對應路徑的配置

官方原話:當微應用資訊註冊完之後,一旦瀏覽器的url發生變化,便會自動觸發qiankun的匹配邏輯,所有activeRule規則匹配上的微應用就會被插入到指定的container中,同時依次呼叫微應用暴露出的生命週期鉤子。

  1. 新建subContainer元件,程式碼如下:
// subContainer.vue
<template>
 <div id="sub-container"></div>
</template>

<script setup>
import { start } from "qiankun";
import { registerApps } from "@/qiankun";

onMounted(()=>{
   if (!window.qiankunStarted) {
     window.qiankunStarted = true;
     registerApps();
     start({
       prefetch: false, // 是否開啟預載入, 預設為 true
       // sandbox: {
       // strictStyleIsolation: true    // 開啟嚴格的樣式隔離模式
       //   experimentalStyleIsolation: true // 樣式隔離
       // }
     });
   }
})
</script>
  1. 路由表配置如下:
// router/xxx.js
export const routes = [
   // ...
   {
       // history模式需要通配所有路由,
       // 注意:此處xxx需要匹配上面的微前端配置裡的activeRule欄位
       path: "/xxx/:pathMatch(.*)*",
       name: "xxxxx",
       meta: {},
       component: () => import("@/views/subContainer")
   },
]

微應用

  • vue3 + vite,路由模式為history

  • 新建qiankun相關配置檔案

    1. 安裝vite-plugin-qiankun外掛(qiankun目前沒有支援vite的文件)

    npm i vite-plugin-qiankun -D

    1. vite.config.js中配置外掛
    // vite.config.js
    import qiankun from "vite-plugin-qiankun";

    export default defineConfig() => {
        const { VITE_POWERED_BY_QIANKUN, VITE_PUBLIC_PATH } = env;
        return {
            base: VITE_PUBLIC_PATH,    // 絕對路徑,線上會有跨域問題
            plugins: [
                // VITE_POWERED_BY_QIANKUN qiankun的路徑字首
                qiankun(VITE_POWERED_BY_QIANKUN, {
                  useDevMode: true
                })
            ]
        }
    }
  1. 匯出對應的生命週期鉤子,以及初始化方式
   // qiankun.js
   import { renderWithQiankun, qiankunWindow } from "vite-plugin-qiankun/dist/helper";

   export const render = (app, container) => {
     app.mount(container ? container.querySelector("#app") : "#app");
   };
   export const initQianKun = (app) => {
     renderWithQiankun({
       mount(props) {
         const { container } = props;
         render(app, container);
       },
       bootstrap() {
         console.log("bootstrap");
       },
       unmount() {
         app.unmount();
       }
     });
   };
   // 初始化,判斷是否在子應用的環境中
   export const init = (app) => {
     qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun(app) : render(app);
   };
  • 路由設定,如果是qiankun子應用的環境,增加對應的字首
    // router/index.js
    import { createWebHistory, createRouter } from "vue-router";
    const VITE_POWERED_BY_QIANKUN = import.meta.env.VITE_POWERED_BY_QIANKUN;
    const router = createRouter({
    history: createWebHistory(
    qiankunWindow.__POWERED_BY_QIANKUN__
      ? `/xxx/${VITE_POWERED_BY_QIANKUN}/`
      : "/"
    ),
    routes: constantRoutes,
    scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    }
    return { top: 0 };
    }
    });

    export default router;
  • 初始化應用
    // main.js
    import { createApp } from "vue";
    import { init } from "@/qiankun.js";
    import App from "./App";
    if (window.__POWERED_BY_QIANKUN__) {
      // eslint-disable-next-line no-undef
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    const app = createApp(App);
    init(app);

vue2 + webpack ,路由模式為 hash

  • 新建qiankun相關配置檔案

    1. 配置webpack
     // vue.config.js
    const { name } = require('./package');
    module.exports = {
      // 開發跨域配置
      devServer: {
        headers: {
          'Access-Control-Allow-Origin': '*',
        },
      },
      configureWebpack: {
        output: {
          library: `${name}-[name]`,
          libraryTarget: 'umd', // 把微應用打包成 umd 庫格式
          jsonpFunction: `webpackJsonp_${name}`, // webpack 5 需要把 jsonpFunction 替換成 chunkLoadingGlobal
        },
      },
    };
  1. 匯出對應的生命週期鉤子,以及初始化方式
    // qiankun.js
    import App from './App'
    import router from './router'
    import store from './store'
    import { setToken, setAccessUser, } from "@/utils/auth";

    let instance = null
    export function render(props = {}) {
      const { container } = props
      instance = new Vue({
        router,
        store,
        render: h => h(App)
      }).$mount(container ? container.querySelector('#app') : '#app')
    }

    export async function bootstrap(props) {
      console.log('[vue] vue app bootstraped', props)
    }
    export async function mount(props, ...args) {
      console.log('[vue] props from main framework', props, args);
      setData(props)
      props.onGlobalStateChange((state, prev) => {
        // state: 變更後的狀態; prev 變更前的狀態
        console.log("🚀 ~ props.onGlobalStateChange ~ state:", state)
        setData(state)
      });
      render(props)
    }
    export async function unmount() {
      instance.$destroy()
      instance.$el.innerHTML = ''
      instance = null
    }
    // 拿到主應用傳過來的資料進行處理
    function setData(state){
      if(state['token']){
        setToken(state.token)
      }
      if(state['accessUser']) {
        setAccessUser(state.accessUser)
      }
    }

    export default render
  • 路由配置

   // router/index.js
   import Router from 'vue-router'
   export const constantRouterMap = [ ... ]
   export default new Router({
     scrollBehavior: () => ({ y: 0 }),
     base: window.__POWERED_BY_QIANKUN__ ? '/xxx/report/' : '/',
     routes: constantRouterMap
   })
  • 初始化應用
  import render from "./qiankun";
  // 匯出所有的鉤子
  export * from "./qiankun";
  if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  }else {
  // 獨立執行時
    render();
  }

總結

  1. 部署上線需要注意資源路徑的問題
  2. 不同伺服器下的專案需要處理跨域問題
  3. qiankun還有手動載入微應用的方式,暫時還沒有用到,可檢視官方文件

本文轉載於:https://juejin.cn/post/7395387245313163327

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

記錄--微前端qiankun接入vue2&vue3專案

相關文章