淺析VUE專案如何使用qiankun搭建微前端架構
微前端就是應用分割
,獨立執行
,獨立部署
,將原本把所有功能集中於一個專案中的方式轉變為把功能按業務劃分成一個主專案和多個子專案,每個子專案負責自身功能,同時具備和其它子專案和主專案進行通訊的能力,達到更細化更易於管理的目的。
總的來說微前端就是:一個完整應用劃分成一個主應用和一個或多個微應用,應用間相互獨立,可相互通訊。
那麼我將使用 微前端框架qiankun 來進行微前端專案搭建。
qiankun官方文件:https://qiankun.umijs.org/zh/guide/getting-started
一、主應用改造 —— 安裝及使用 qiankun
主應用專案必須安裝 qiankun:npm i qiankun -S
在主應用的src資料夾下新建一個 micros 資料夾,在micros資料夾新建index.js、app.js
1、index.js —— 主要是匯出方法及生命週期鉤子,用於在主應用中註冊微應用
import {
registerMicroApps,
addGlobalUncaughtErrorHandler,
start,
} from "qiankun";// 微應用註冊資訊
import apps from "./app";
registerMicroApps(apps, {
beforeLoad: (app) => {
console.log("before load ", app.name);
return Promise.resolve();
},
afterMount: (app) => {
console.log("after mount", app.name);
return Promise.resolve();
},
});
addGlobalUncaughtErrorHandler((event) => {
console.error(event);
const { message: msg } = event
if (msg && msg.includes("died in status LOADING_SOURCE_CODE ")) {
console.error("微應用載入失敗,請檢查應用是否可執行");
}
});
export default start;
這裡用到了官方的幾個api:
(1)registerMicroApps
:包含兩個引數,第一個引數是微應用的一些註冊資訊,第二個引數是全域性的微應用生命週期鉤子。:
(2)addGlobalUncaughtErrorHandler
:全域性的未捕獲異常處理器,微應用發生報錯的時候亦可以用這個api捕捉。
(3)start
:我們用來啟動qiankun的方法,包含一個引數,具體的引數用途不再詳述。
以上詳細的api請點選檢視官網API文件:https://qiankun.umijs.org/zh/api
2、app.js —— 主要是寫一些微應用的資訊
const apps = [
{
name: "micro-app-order",
entry: "//localhost:8088",
container: "#order-container",
activeRule: "/micorder",
},
];
export default apps;
app.js匯出的是上面registerMicroApps
的第一個引數,是一個物件陣列,其中陣列每個欄位的作用如下:
(1)name
:微應用的名稱,後面改造微應用的時候一定要與這個name對應
(2)entry
:微應用執行的域名加埠,我用的是本地8088埠
(3)container
:啟動微應用需要一個dom容器,裡面就是這個dom容器的 id
(4)activeRule
:觸發啟動微應用的規則,當檢測到url中含有activeRule的值時,將啟動微應用
當微應用資訊註冊完之後,一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯,所有 activeRule 規則匹配上的微應用就會被插入到指定的 container 中,同時依次呼叫微應用暴露出的生命週期鉤子。
3、在主應用中新增微應用 dom 容器
<template>
<div id="main-app">
<router-view/>
<!-- 新新增,微應用的容器 -->
<div id="order-container"></div>
</div>
</template>
需要注意的是:第一個:可以看到我上面的 id 改為了 main-app,這個預設的 app 最好改一下,主應用子應用不要重複,否則會有 CSS 汙染問題。第二個就是微應用的容器 id : order-container 就是與我們上面定義的 apps 裡的 container 裡的 id 要保持一致。
4、main.js 改造 —— 主要就是匯入 start 方法,然後執行,啟動qiankun
......
import start from '@/micros'
......
start()
這個改造就比較簡單,引入 start() 方法,然後執行即可。
做完上述改造之後,重新整理一下瀏覽器,發現主應用和改造前並無差異!
二、微應用改造
官網寫了:微應用不需要額外安裝任何其他依賴即可接入 qiankun 主應用。
1、匯出相應的生命週期鉤子
微應用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 匯出 bootstrap
、mount
、unmount
三個生命週期鉤子,以供主應用在適當的時機呼叫。
/**
* bootstrap 只會在微應用初始化的時候呼叫一次,下次微應用重新進入時會直接呼叫 mount 鉤子,不會再重複觸發 bootstrap。
* 通常我們可以在這裡做一些全域性變數的初始化,比如不會在 unmount 階段被銷燬的應用級別的快取等。
*/
export async function bootstrap() {
console.log('react app bootstraped');
}
/**
* 應用每次進入都會呼叫 mount 方法,通常我們在這裡觸發應用的渲染方法
*/
export async function mount(props) {
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/**
* 應用每次 切出/解除安裝 會呼叫的方法,通常在這裡我們會解除安裝微應用的應用例項
*/
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(
props.container ? props.container.querySelector('#root') : document.getElementById('root'),
);
}
/**
* 可選生命週期鉤子,僅使用 loadMicroApp 方式載入微應用時生效
*/
export async function update(props) {
console.log('update props', props);
}
這是微應用改造前的main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
下面我們來改造一下main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
Vue.config.productionTip = false
// 新增:用於儲存vue例項
let instance = null;
// 新增:動態設定 webpack publicPath,防止資源加載出錯
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
/** * 新增: * 渲染函式 * 兩種情況:主應用生命週期鉤子中執行 / 微應用單獨啟動時執行 */
function render() {
// 掛載應用
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#micro-app-child");
}
/**
* 新增:
* bootstrap 只會在微應用初始化的時候呼叫一次,
下次微應用重新進入時會直接呼叫 mount 鉤子,不會再重複觸發 bootstrap。
* 通常我們可以在這裡做一些全域性變數的初始化,比如不會在 unmount 階段被銷燬的應用級別的快取等。
*/
export async function bootstrap() {
console.log("VueMicroApp bootstraped");
}
/**
* 新增:
* 應用每次進入都會呼叫 mount 方法,通常我們在這裡觸發應用的渲染方法
*/
export async function mount(props) {
console.log("VueMicroApp mount", props);
render(props);
}
/**
* 新增:
* 應用每次 切出/解除安裝 會呼叫的方法,通常在這裡我們會解除安裝微應用的應用例項
*/
export async function unmount() {
console.log("VueMicroApp unmount");
instance.$destroy();
instance = null;
}
// 新增:獨立執行時,直接掛載應用
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
特別需要注意的是,render方法中我把$mount後的引數改為了#micro-app-child
,這是為了區分主應用和微應用中index.html
的根id,所以微應用中的public資料夾的index.html
也要改為micro-app-child
2、配置微應用的打包工具
除了程式碼中暴露出相應的生命週期鉤子之外,為了讓主應用能正確識別微應用暴露出來的一些資訊,微應用的打包工具需要增加如下配置:
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
所以我們還需要對webpack配置進行改造,微應用根目錄下的vue.config.js
檔案
const path = require("path");
module.exports = {
devServer: {
// 監聽埠
port: 8088,
// 關閉主機檢查,使微應用可以被 fetch
disableHostCheck: true,
// 配置跨域請求頭,解決開發環境的跨域問題
headers: {
"Access-Control-Allow-Origin": "*",
},
},
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
output: {
// 微應用的包名,這裡與主應用中註冊的微應用名稱一致
library: "micro-app-order",
// 將你的 library 暴露為所有的模組定義下都可執行的方式
libraryTarget: "umd",
// 按需載入相關,設定為 webpackJsonp_MicroAppOrde 即可
jsonpFunction: `webpackJsonp_micro-app-order`,
},
},
};
主要是要設定下 output 的配置,其他的配置按原專案的即可。
3、然後還要改造一下我們的路由
路由主要的改動就是每個path
都添加了一個microPath
變數,用於檢測是否由微前端改動,相應的路由守衛也要新增microPath
變數,另外微應用的login
跳轉的時候也要加上microPath
判斷
這裡我採用 history 模式,只需要加上 base 即可
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/micorder' : '/',
routes,
scrollBehavior(to, from, savedPosition) { //設定滾動行為
......
}
})
4、改造效果
做完以上步驟改造,最後重啟一下我們的微應用,然後在主應用上加上一些跳轉到微應用頁面的路由。我們看看改造效果:
<div id="main-app">
<div class="fix-box">
<!-- 主應用首頁 -->
<router-link to="/">首頁</router-link>
<!-- 微應用商品頁 -->
<router-link to="/micorder/foods">商品</router-link>
<!-- 微應用訂單頁 -->
<router-link to="/micorder/ord">訂單</router-link>
</div>
<router-view/>
<!-- 新新增,微應用的容器 -->
<div id="order-container"></div>
</div>
子應用獨立執行
主應用 demo 執行
主應用裡子應用執行