微前端qiankun改造日漸龐大的項目教程
項目背景
很多小夥伴在工作中都碰到過和我一樣的場景,手上的某個項目越來越大,眼看著每次build時間越來越長,吐瞭🐱。在杭州某獨角獸我碰到瞭這樣的一個項目,他叫運營後臺,聽名字就知道,他的主要用戶是運營人員。問題就是隨著公司業務的越來越多,這個運營後臺承擔的已經不是某一塊業務瞭,而是所有業務的運營操作的中後臺都在這上面。你可以這樣理解,這個系統的每個一級菜單都是一塊獨立的業務,相互之間沒有任何瓜葛;按常規的理解,這應該是單獨的每一個project比較合理,但是正因為他的用戶又都是公司的同一群人,他們早已經習慣就在運營後臺上去找自己的菜單進行業務操作,拒不接受在他的收藏夾裡多出來好幾個項目地址。那我們有沒有一個辦法讓我們的項目更好維護,又能讓用戶不改變他們使用同一個項目的期望呢。這就是寫這篇文章的初衷,就是微前端!!🙌🙌
微前端的好處
除瞭可以解決上面的問題以外,你想想,隻要我們把項目改造成瞭微前端,那每個業務都是獨立的project,隻不過最終用戶都是在一個主項目裡去使用,那我們每個project的技術棧也不用定死瞭,就不存在老項目的技術棧是vue,以至於後面的項目都必須要用vue瞭,你也可以用react,這就很香不是嗎。微前端的原理就是項目被拆成瞭父子關系,通過基座去引用瞭子應用,子應用之間是互相隔離的😁😁
qiankun
可能是你見過最完善的微前端解決方案🧐——官方是這麼介紹的,基於single-spa,這裡我就不詳細介紹瞭,感興趣的去看看文檔,地址丟給你
- qiankun
- single-spa
改造過程
首先我先用vue2-admin-cli——我自己做的腳手架工具,創建兩個vue-admin項目來演示,一個作為qiankun基座,另外一個就是我要引用的子應用。
全局安裝腳手架 npm install -g vue2-admin-cli or yarn global add vue2-admin-cli 創建項目 vue2-admin-cli init <project_name> 安裝依賴 yarn 啟動項目 yarn serve
運行起來就是這樣的
現在我們開始分別改造基座qiankun-base和子應用qiankun-vue,我想達到的效果是主應用qiankun-base隻保留header sider footer的一個基本layout的佈局,content部分全部加載子應用
qiankun-base
yarn add qiankun # 或者 npm i qiankun -S
修改package.json啟動命令修改啟動端口
"serve": "vue-cli-service serve –port 80 –open"
src/router/index.ts修改路由模式為history
const createRouter = () => new VueRouter({ mode: "history", routes: routes as any, });
修改vue.config.js 我這裡之前用的路由模式是hash 上線配置瞭publicPath 導致改為history以後靜態資源加載路徑有問題所以修改
module.exports = { // publicPath: "./", devServer: { disableHostCheck: true, // 關閉host檢查 }, };
在入口文件src/main.ts下註冊微應用並啟動:
import { registerMicroApps, start } from "qiankun"; registerMicroApps([ { name: "qiankunVue", entry: "//localhost:8080", //子應用的啟動端口修改為8080,基座使用80,不要相同 container: "#qiankunVue", //加載子應用的容器 activeRule: "/qiankunVue", //路由匹配規則 }, ]); // 啟動 qiankun start();
在你要放置子應用的位置增加一個容器用於加載子應用
src/components/layout/index.vue
<template> <el-container direction="vertical" style="height: 100%"> <Header /> <el-container style="overflow: auto"> <el-aside width="250px"> <Menu /> </el-aside> <el-main> <el-breadcrumb separator-class="el-icon-arrow-right" v-if="showBreadcrumb" > <template v-for="(route, index) in matchedRoutes"> <el-breadcrumb-item v-if=" (route.meta && route.meta.breadcrumbTo === false) || index === matchedRoutes.length - 1 " :key="route.path" > {{ route.meta.title }} </el-breadcrumb-item> <el-breadcrumb-item v-else :key="route.path" :to="{ path: route.path }" > {{ route.meta.title }} </el-breadcrumb-item> </template> </el-breadcrumb> <!-- 本身的路由加載 --> <router-view style="margin-top: 20px" /> <!-- 子應用加載容器 --> <div id="qiankunVue" style="width: 100%; height: 100%" /> </el-main> </el-container> </el-container> </template>
將主應用之前的路由配置進行修改,不渲染自己的內容瞭,因為要改成去加載子應用的內容才是我們想要的,我的左側菜單欄sider也是用路由配置這份文件生成的,所以我隻需要註釋這些路由要渲染的components就行,讓他隻充當一個生成sider菜單欄的作用,但是註意要保留容器所在的layout,因為我的子應用加載容器在這裡面,加載子應用之前你必須保證容器被加載瞭
src/router/config.ts
const routes: Array<IBaseRouter> = [ { path: "/", redirect: "/home", hidden: true, }, { path: "/login", name: "login", hidden: true, component: () => import("../views/Login.vue"), }, //保證子應用加載時容器頁面必須加載 { path: "/qiankunVue/*", name: "qiankunVue", hidden: true, component: Layout, }, { path: "/qiankunVue/home", name: "home", component: Layout, redirect: "/qiankunVue/home/index", meta: { title: "首頁", icon: "el-icon-s-home", }, children: [ { path: "index", name: "index", hidden: true, // component: () => import("../views/Home.vue"), meta: { title: "首頁", breadcrumb: false, }, }, { path: "bar/:width/:height", name: "bar", props: true, hidden: true, // component: () => import("@/components/echarts/Bar.vue"), meta: { title: "柱狀圖", activeMenu: "/home/index", }, }, { path: "pie/:width/:height", name: "pie", props: true, hidden: true, // component: () => import("@/components/echarts/Pie.vue"), meta: { title: "餅圖", activeMenu: "/home/index", }, }, { path: "line/:width/:height", name: "line", props: true, hidden: true, // component: () => import("@/components/echarts/Line.vue"), meta: { title: "折線圖", activeMenu: "/home/index", }, }, ], }, ..... { path: "*", redirect: "/error/404", hidden: true, }, ]; export default routes;
改完之後刷新看一看,這樣基座項目就改造好瞭,保留瞭基本頁面的框架,中間的內容到時候都由子應用來填充就行瞭
qiankun-vue
修改package.json啟動命令修改啟動端口
"serve": "vue-cli-service serve –port 8080 –open"
入口文件 src/main.ts 修改
let vm: any = null; function render(props: any = {}) { const { container } = props; vm = new Vue({ router, store, render: (h) => h(App), }).$mount(container ? container.querySelector("#app") : "#app"); } // 在被qiankun引用時 修改運行時的 `publicPath` if ((window as any).__POWERED_BY_QIANKUN__) { __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } // 獨立運行時 if (!(window as any).__POWERED_BY_QIANKUN__) { render(); } 導出三個生命周期函數 export async function bootstrap() { console.log("[vue] vue app bootstraped"); } export async function mount(props: any) { console.log("[vue] props from main framework", props); render(props); } export async function unmount() { vm.$destroy(); vm.$el.innerHTML = ""; vm = null; } export default vm;
src/router/index.ts修改路由模式並增加base(和主應用設置的activeRule一致)
const createRouter = () => new VueRouter({ mode: "history", base: "/qiankunVue", routes: routes as any });
打包配置修改(`vue.config.js`)
module.exports = { devServer: { disableHostCheck: true, // 關閉host檢查 headers: { "Access-Control-Allow-Origin": "*", // 防止加載時跨域 }, }, configureWebpack: { output: { library: "qiankunVue", libraryTarget: "umd", // 把微應用打包成 umd 庫格式 }, }, };
基座和子應用都修改完以後刷新看看,控制臺報錯瞭
立馬查看官方文檔,發現是因為我的子應用加載容器在基座的某個路由頁面即我的layout裡面,文檔裡指出必須保證微應用加載時主應用這個路由頁面也加載瞭,就很喜歡這種文檔😄😄,於是立馬改一改
註釋之前qiankun-base註冊子應用時的啟動qiankun命令,改到路由頁面layout裡面啟動
src/main.ts
// 啟動 qiankun //start(); src/components/layout/index.vue import { start } from "qiankun"; mounted() { if (!(window as any).qiankunStarted) { (window as any).qiankunStarted = true; start(); } }
重新刷新看看,成功瞭🔥🔥,多少有點舒服瞭
接下來要做的就是把子應用再改造一下,在qiankun中就隻需要展示子應用content的內容,單獨運行的時候為瞭方便調試我們就保留layout佈局。看瞭這麼久的官方文檔,我當然知道用它就可以做出判斷__POWERED_BY_QIANKUN__,思路很清晰,沖他👍👍
qiankun-vue
src/components/layout/index.vue
<template> <el-container direction="vertical" v-if="isQiankun"> <el-main> <el-breadcrumb separator-class="el-icon-arrow-right" v-if="showBreadcrumb" > <template v-for="(route, index) in matchedRoutes"> <el-breadcrumb-item v-if=" (route.meta && route.meta.breadcrumbTo === false) || index === matchedRoutes.length - 1 " :key="route.path" > {{ route.meta.title }} <!-- {{ route.path }} --> </el-breadcrumb-item> <el-breadcrumb-item v-else :key="route.path" :to="{ path: route.path }" > {{ route.meta.title }} <!-- {{ route.path }} --> </el-breadcrumb-item> </template> </el-breadcrumb> <router-view style="margin-top: 20px" /> </el-main> </el-container> <el-container direction="vertical" style="height: 100%" v-else> <Header /> <el-container style="overflow: auto"> <el-aside width="250px"> <Menu /> </el-aside> <el-main> <el-breadcrumb separator-class="el-icon-arrow-right" v-if="showBreadcrumb" > <template v-for="(route, index) in matchedRoutes"> <el-breadcrumb-item v-if=" (route.meta && route.meta.breadcrumbTo === false) || index === matchedRoutes.length - 1 " :key="route.path" > {{ route.meta.title }} <!-- {{ route.path }} --> </el-breadcrumb-item> <el-breadcrumb-item v-else :key="route.path" :to="{ path: route.path }" > {{ route.meta.title }} <!-- {{ route.path }} --> </el-breadcrumb-item> </template> </el-breadcrumb> <router-view style="margin-top: 20px" /> </el-main> </el-container> </el-container> </template> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; import Header from "./Header.vue"; import Menu from "./Menu.vue"; // import { IBaseRouter } from "@/router/config"; @Component({ name: "Layout", components: { Header, Menu }, }) export default class Layout extends Vue { private get showBreadcrumb() { return this.$route?.meta?.breadcrumbAll !== false; } private get matchedRoutes() { return this.$route.matched?.filter( (v) => v.meta?.title && v?.meta?.breadcrumb !== false ); } private get isQiankun() { return (window as any).__POWERED_BY_QIANKUN__; } } </script> <style lang="less" scoped></style>
至此完結撒花,改造結束❤️❤️,看看效果 基座正常展示子應用
子應用單獨運行也正常展示,並且絲毫不影響開發體驗
項目地址
qiankun-base qiankun基座
qiankun-vue qiankun子應用
vue-admin ## vue 中後臺系統解決方案
vue2-admin-cli vue2-admin-cli是vue-admin的cli腳手架工具,支持快速搭建企業級中後臺項目模板
結尾
關於父子通信這個示例就不做概述,有興趣的可以自己看看文檔
以上就是微前端qiankun改造日漸龐大的項目教程的詳細內容,更多關於微前端qiankun改造龐大項目的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- vue router-view的嵌套顯示實現
- vue.js使用Element-ui中實現導航菜單
- vue遞歸組件實現elementUI多級菜單
- vuejs路由的傳參及路由props配置詳解
- vue實現動態路由詳細