微前端系列文章:css
本系列其餘文章計劃一到兩個月內完成,點個 關注
不迷路。html
計劃以下:前端
你們好~vue
本文是基於 qiankun
的微前端最佳實踐系列文章之 從 0 到 1 篇
,本文將分享如何使用 qiankun
如何搭建主應用基座,而後接入不一樣技術棧的微應用,完成微前端架構的從 0 到 1。node
本教程採用 Vue
做爲主應用基座,接入不一樣技術棧的微應用。若是你不懂 Vue
也不要緊,咱們在搭建主應用基座的教程儘可能不涉及 Vue
的 API
,涉及到 API
的地方都會給出解釋。react
注意:
qiankun
屬於無侵入性的微前端框架,對主應用基座和微應用的技術棧都沒有要求。
咱們在本教程中,接入了多技術棧 微應用
的 主應用
最終效果圖以下:jquery
咱們以 實戰案例 - feature-inject-sub-apps 分支 (案例是以 Vue
爲基座的主應用,接入多個微應用) 爲例,來介紹一下如何在 qiankun
中如何接入不一樣技術棧的微應用。webpack
咱們先使用 vue-cli
生成一個 Vue
的項目,初始化主應用。git
vue-cli 是Vue
官方提供的腳手架工具,用於快速搭建一個Vue
項目。若是你想跳過這一步,能夠直接clone
實戰案例 - feature-inject-sub-apps 分支 的代碼。
將普通的項目改形成 qiankun
主應用基座,須要進行三步操做:github
qiankun
;咱們先在主應用中建立微應用的承載容器,這個容器規定了微應用的顯示區域,微應用將在該容器內渲染並顯示。
咱們先設置路由,路由文件規定了主應用自身的路由匹配規則,代碼實現以下:
// micro-app-main/src/routes/index.ts import Home from "@/pages/home/index.vue"; const routes = [ { /** * path: 路徑爲 / 時觸發該路由規則 * name: 路由的 name 爲 Home * component: 觸發路由時加載 `Home` 組件 */ path: "/", name: "Home", component: Home, }, ]; export default routes; // micro-app-main/src/main.ts //... import Vue from "vue"; import VueRouter from "vue-router"; import routes from "./routes"; /** * 註冊路由實例 * 即將開始監聽 location 變化,觸發路由規則 */ const router = new VueRouter({ mode: "history", routes, }); // 建立 Vue 實例 // 該實例將掛載/渲染在 id 爲 main-app 的節點上 new Vue({ router, render: (h) => h(App), }).$mount("#main-app");
從上面代碼能夠看出,咱們設置了主應用的路由規則,設置了 Home
主頁的路由匹配規則。
咱們如今來設置主應用的佈局,咱們會有一個菜單和顯示區域,代碼實現以下:
// micro-app-main/src/App.vue //... export default class App extends Vue { /** * 菜單列表 * key: 惟一 Key 值 * title: 菜單標題 * path: 菜單對應的路徑 */ menus = [ { key: "Home", title: "主頁", path: "/", }, ]; }
上面的代碼是咱們對菜單配置的實現,咱們還須要實現基座和微應用的顯示區域(以下圖)
咱們來分析一下上面的代碼:
第 5 行
:主應用菜單,用於渲染菜單;第 9 行
:主應用渲染區。在觸發主應用路由規則時(由路由配置表的 $route.name
判斷),將渲染主應用的組件;第 10 行
:微應用渲染區。在未觸發主應用路由規則時(由路由配置表的 $route.name
判斷),將渲染微應用節點;從上面的分析能夠看出,咱們使用了在路由表配置的 name
字段進行判斷,判斷當前路由是否爲主應用路由,最後決定渲染主應用組件或是微應用節點。
因爲篇幅緣由,樣式實現代碼就不貼出來了,最後主應用的實現效果以下圖所示:
從上圖能夠看出,咱們主應用的組件和微應用是顯示在同一片內容區域,根據路由規則決定渲染規則。
在構建好了主框架後,咱們須要使用 qiankun
的 registerMicroApps
方法註冊微應用,代碼實現以下:
// micro-app-main/src/micro/apps.ts // 此時咱們尚未微應用,因此 apps 爲空 const apps = []; export default apps; // micro-app-main/src/micro/index.ts // 一個進度條插件 import NProgress from "nprogress"; import "nprogress/nprogress.css"; import { message } from "ant-design-vue"; import { registerMicroApps, addGlobalUncaughtErrorHandler, start, } from "qiankun"; // 微應用註冊信息 import apps from "./apps"; /** * 註冊微應用 * 第一個參數 - 微應用的註冊信息 * 第二個參數 - 全局生命週期鉤子 */ registerMicroApps(apps, { // qiankun 生命週期鉤子 - 微應用加載前 beforeLoad: (app: any) => { // 加載微應用前,加載進度條 NProgress.start(); console.log("before load", app.name); return Promise.resolve(); }, // qiankun 生命週期鉤子 - 微應用掛載後 afterMount: (app: any) => { // 加載微應用前,進度條加載完成 NProgress.done(); console.log("after mount", app.name); return Promise.resolve(); }, }); /** * 添加全局的未捕獲異常處理器 */ addGlobalUncaughtErrorHandler((event: Event | string) => { console.error(event); const { message: msg } = event as any; // 加載失敗時提示 if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) { message.error("微應用加載失敗,請檢查應用是否可運行"); } }); // 導出 qiankun 的啓動函數 export default start;
從上面能夠看出,咱們的微應用註冊信息在 apps
數組中(此時爲空,咱們在後面接入微應用時會添加微應用註冊信息),而後使用 qiankun
的 registerMicroApps
方法註冊微應用,最後導出了 start
函數,註冊微應用的工做就完成啦!
咱們在註冊好了微應用,導出 start
函數後,咱們須要在合適的地方調用 start
啓動主應用。
咱們通常是在入口文件啓動 qiankun
主應用,代碼實現以下:
// micro-app-main/src/main.ts //... import startQiankun from "./micro"; startQiankun();
最後,啓動咱們的主應用,效果圖以下:
由於咱們尚未註冊任何微應用,因此這裏的效果圖和上面的效果圖是同樣的。
到這一步,咱們的主應用基座就建立好啦!
咱們如今的主應用基座只有一個主頁,如今咱們須要接入微應用。
qiankun
內部經過 import-entry-html
加載微應用,要求微應用須要導出生命週期鉤子函數(見下圖)。
從上圖能夠看出,qiankun
內部會校驗微應用的生命週期鉤子函數,若是微應用沒有導出這三個生命週期鉤子函數,則微應用會加載失敗。
若是咱們使用了腳手架搭建微應用的話,咱們能夠經過 webpack
配置在入口文件處導出這三個生命週期鉤子函數。若是沒有使用腳手架的話,也能夠直接在微應用的 window
上掛載這三個生命週期鉤子函數。
如今咱們來接入咱們的各個技術棧微應用吧!
注意,下面的內容對相關技術棧
API
不會再有過多介紹啦,若是你要接入不一樣技術棧的微應用,最好要對該技術棧有一些基礎瞭解。
Vue
微應用咱們以 實戰案例 - feature-inject-sub-apps 分支 爲例,咱們在主應用的同級目錄(micro-app-main
同級目錄),使用 vue-cli
先建立一個 Vue
的項目,在命令行運行以下命令:
vue create micro-app-vue
本文的 vue-cli
選項以下圖所示,你也能夠根據本身的喜愛選擇配置。
在新建項目完成後,咱們建立幾個路由頁面再加上一些樣式,最後效果以下:
在建立好了 Vue
微應用後,咱們能夠開始咱們的接入工做了。首先咱們須要在主應用中註冊該微應用的信息,代碼實現以下:
// micro-app-main/src/micro/apps.ts const apps = [ /** * name: 微應用名稱 - 具備惟一性 * entry: 微應用入口 - 經過該地址加載微應用 * container: 微應用掛載節點 - 微應用加載完成後將掛載在該節點上 * activeRule: 微應用觸發的路由規則 - 觸發路由規則後將加載該微應用 */ { name: "VueMicroApp", entry: "//localhost:10200", container: "#frame", activeRule: "/vue", }, ]; export default apps;
經過上面的代碼,咱們就在主應用中註冊了咱們的 Vue
微應用,進入 /vue
路由時將加載咱們的 Vue
微應用。
咱們在菜單配置處也加入 Vue
微應用的快捷入口,代碼實現以下:
// micro-app-main/src/App.vue //... export default class App extends Vue { /** * 菜單列表 * key: 惟一 Key 值 * title: 菜單標題 * path: 菜單對應的路徑 */ menus = [ { key: "Home", title: "主頁", path: "/", }, { key: "VueMicroApp", title: "Vue 主頁", path: "/vue", }, { key: "VueMicroAppList", title: "Vue 列表頁", path: "/vue/list", }, ]; }
菜單配置完成後,咱們的主應用基座效果圖以下
在主應用註冊好了微應用後,咱們還須要對微應用進行一系列的配置。首先,咱們在 Vue
的入口文件 main.js
中,導出 qiankun
主應用所須要的三個生命週期鉤子函數,代碼實現以下:
從上圖來分析:
第 6 行
:webpack
默認的 publicPath
爲 ""
空字符串,會基於當前路徑來加載資源。咱們在主應用中加載微應用時須要從新設置 publicPath
,這樣才能正確加載微應用的相關資源。(public-path.js
具體實如今後面)第 21 行
:微應用的掛載函數,在主應用中運行時將在 mount
生命週期鉤子函數中調用,能夠保證在沙箱內運行。第 38 行
:微應用獨立運行時,直接執行 render
函數掛載微應用。第 46 行
:微應用導出的生命週期鉤子函數 - bootstrap
。第 53 行
:微應用導出的生命週期鉤子函數 - mount
。第 61 行
:微應用導出的生命週期鉤子函數 - unmount
。完整代碼實現以下:
// micro-app-vue/src/public-path.js if (window.__POWERED_BY_QIANKUN__) { // 動態設置 webpack publicPath,防止資源加載出錯 // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } // micro-app-vue/src/main.js import Vue from "vue"; import VueRouter from "vue-router"; import Antd from "ant-design-vue"; import "ant-design-vue/dist/antd.css"; import "./public-path"; import App from "./App.vue"; import routes from "./routes"; Vue.use(VueRouter); Vue.use(Antd); Vue.config.productionTip = false; let instance = null; let router = null; /** * 渲染函數 * 兩種狀況:主應用生命週期鉤子中運行 / 微應用單獨啓動時運行 */ function render() { // 在 render 中建立 VueRouter,能夠保證在卸載微應用時,移除 location 事件監聽,防止事件污染 router = new VueRouter({ // 運行在主應用中時,添加路由命名空間 /vue base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/", mode: "history", routes, }); // 掛載應用 instance = new Vue({ router, render: (h) => h(App), }).$mount("#app"); } // 獨立運行時,直接掛載應用 if (!window.__POWERED_BY_QIANKUN__) { render(); } /** * 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; router = null; }
在配置好了入口文件 main.js
後,咱們還須要配置 webpack
,使 main.js
導出的生命週期鉤子函數能夠被 qiankun
識別獲取。
咱們直接配置 vue.config.js
便可,代碼實現以下:
// micro-app-vue/vue.config.js const path = require("path"); module.exports = { devServer: { // 監聽端口 port: 10200, // 關閉主機檢查,使微應用能夠被 fetch disableHostCheck: true, // 配置跨域請求頭,解決開發環境的跨域問題 headers: { "Access-Control-Allow-Origin": "*", }, }, configureWebpack: { resolve: { alias: { "@": path.resolve(__dirname, "src"), }, }, output: { // 微應用的包名,這裏與主應用中註冊的微應用名稱一致 library: "VueMicroApp", // 將你的 library 暴露爲全部的模塊定義下均可運行的方式 libraryTarget: "umd", // 按需加載相關,設置爲 webpackJsonp_VueMicroApp 便可 jsonpFunction: `webpackJsonp_VueMicroApp`, }, }, };
咱們須要重點關注一下 output
選項,當咱們把 libraryTarget
設置爲 umd
後,咱們的 library
就暴露爲全部的模塊定義下均可運行的方式了,主應用就能夠獲取到微應用的生命週期鉤子函數了。
在 vue.config.js
修改完成後,咱們從新啓動 Vue
微應用,而後打開主應用基座 http://localhost:9999
。咱們點擊左側菜單切換到微應用,此時咱們的 Vue
微應用被正確加載啦!(見下圖)
此時咱們打開控制檯,能夠看到咱們所執行的生命週期鉤子函數(見下圖)
到這裏,Vue
微應用就接入成功了!
React
微應用咱們以 實戰案例 - feature-inject-sub-apps 分支 爲例,咱們在主應用的同級目錄(micro-app-main
同級目錄),使用 react-create-app
先建立一個 React
的項目,在命令行運行以下命令:
npx create-react-app micro-app-react
在項目建立完成後,咱們在根目錄下添加 .env
文件,設置項目監聽的端口,代碼實現以下:
# micro-app-react/.env PORT=10100 BROWSER=none
而後,咱們建立幾個路由頁面再加上一些樣式,最後效果以下:
在建立好了 React
微應用後,咱們能夠開始咱們的接入工做了。首先咱們須要在主應用中註冊該微應用的信息,代碼實現以下:
// micro-app-main/src/micro/apps.ts const apps = [ /** * name: 微應用名稱 - 具備惟一性 * entry: 微應用入口 - 經過該地址加載微應用 * container: 微應用掛載節點 - 微應用加載完成後將掛載在該節點上 * activeRule: 微應用觸發的路由規則 - 觸發路由規則後將加載該微應用 */ { name: "ReactMicroApp", entry: "//localhost:10100", container: "#frame", activeRule: "/react", }, ]; export default apps;
經過上面的代碼,咱們就在主應用中註冊了咱們的 React
微應用,進入 /react
路由時將加載咱們的 React
微應用。
咱們在菜單配置處也加入 React
微應用的快捷入口,代碼實現以下:
// micro-app-main/src/App.vue //... export default class App extends Vue { /** * 菜單列表 * key: 惟一 Key 值 * title: 菜單標題 * path: 菜單對應的路徑 */ menus = [ { key: "Home", title: "主頁", path: "/", }, { key: "ReactMicroApp", title: "React 主頁", path: "/react", }, { key: "ReactMicroAppList", title: "React 列表頁", path: "/react/list", }, ]; }
菜單配置完成後,咱們的主應用基座效果圖以下
在主應用註冊好了微應用後,咱們還須要對微應用進行一系列的配置。首先,咱們在 React
的入口文件 index.js
中,導出 qiankun
主應用所須要的三個生命週期鉤子函數,代碼實現以下:
從上圖來分析:
第 5 行
:webpack
默認的 publicPath
爲 ""
空字符串,會基於當前路徑來加載資源。咱們在主應用中加載微應用時須要從新設置 publicPath
,這樣才能正確加載微應用的相關資源。(public-path.js
具體實如今後面)第 12 行
:微應用的掛載函數,在主應用中運行時將在 mount
生命週期鉤子函數中調用,能夠保證在沙箱內運行。第 17 行
:微應用獨立運行時,直接執行 render
函數掛載微應用。第 25 行
:微應用導出的生命週期鉤子函數 - bootstrap
。第 32 行
:微應用導出的生命週期鉤子函數 - mount
。第 40 行
:微應用導出的生命週期鉤子函數 - unmount
。完整代碼實現以下:
// micro-app-react/src/public-path.js if (window.__POWERED_BY_QIANKUN__) { // 動態設置 webpack publicPath,防止資源加載出錯 // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } // micro-app-react/src/index.js import React from "react"; import ReactDOM from "react-dom"; import "antd/dist/antd.css"; import "./public-path"; import App from "./App.jsx"; /** * 渲染函數 * 兩種狀況:主應用生命週期鉤子中運行 / 微應用單獨啓動時運行 */ function render() { ReactDOM.render(<App />, document.getElementById("root")); } // 獨立運行時,直接掛載應用 if (!window.__POWERED_BY_QIANKUN__) { render(); } /** * bootstrap 只會在微應用初始化的時候調用一次,下次微應用從新進入時會直接調用 mount 鉤子,不會再重複觸發 bootstrap。 * 一般咱們能夠在這裏作一些全局變量的初始化,好比不會在 unmount 階段被銷燬的應用級別的緩存等。 */ export async function bootstrap() { console.log("ReactMicroApp bootstraped"); } /** * 應用每次進入都會調用 mount 方法,一般咱們在這裏觸發應用的渲染方法 */ export async function mount(props) { console.log("ReactMicroApp mount", props); render(props); } /** * 應用每次 切出/卸載 會調用的方法,一般在這裏咱們會卸載微應用的應用實例 */ export async function unmount() { console.log("ReactMicroApp unmount"); ReactDOM.unmountComponentAtNode(document.getElementById("root")); }
在配置好了入口文件 index.js
後,咱們還須要配置路由命名空間,以確保主應用能夠正確加載微應用,代碼實現以下:
// micro-app-react/src/App.jsx const BASE_NAME = window.__POWERED_BY_QIANKUN__ ? "/react" : ""; const App = () => { //... return ( // 設置路由命名空間 <Router basename={BASE_NAME}>{/* ... */}</Router> ); };
接下來,咱們還須要配置 webpack
,使 index.js
導出的生命週期鉤子函數能夠被 qiankun
識別獲取。
咱們須要藉助 react-app-rewired
來幫助咱們修改 webpack
的配置,咱們直接安裝該插件:
npm install react-app-rewired -D
在 react-app-rewired
安裝完成後,咱們還須要修改 package.json
的 scripts
選項,修改成由 react-app-rewired
啓動應用,就像下面這樣
// micro-app-react/package.json //... "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" }
在 react-app-rewired
配置完成後,咱們新建 config-overrides.js
文件來配置 webpack
,代碼實現以下:
const path = require("path"); module.exports = { webpack: (config) => { // 微應用的包名,這裏與主應用中註冊的微應用名稱一致 config.output.library = `ReactMicroApp`; // 將你的 library 暴露爲全部的模塊定義下均可運行的方式 config.output.libraryTarget = "umd"; // 按需加載相關,設置爲 webpackJsonp_VueMicroApp 便可 config.output.jsonpFunction = `webpackJsonp_ReactMicroApp`; config.resolve.alias = { ...config.resolve.alias, "@": path.resolve(__dirname, "src"), }; return config; }, devServer: function (configFunction) { return function (proxy, allowedHost) { const config = configFunction(proxy, allowedHost); // 關閉主機檢查,使微應用能夠被 fetch config.disableHostCheck = true; // 配置跨域請求頭,解決開發環境的跨域問題 config.headers = { "Access-Control-Allow-Origin": "*", }; // 配置 history 模式 config.historyApiFallback = true; return config; }; }, };
咱們須要重點關注一下 output
選項,當咱們把 libraryTarget
設置爲 umd
後,咱們的 library
就暴露爲全部的模塊定義下均可運行的方式了,主應用就能夠獲取到微應用的生命週期鉤子函數了。
在 config-overrides.js
修改完成後,咱們從新啓動 React
微應用,而後打開主應用基座 http://localhost:9999
。咱們點擊左側菜單切換到微應用,此時咱們的 React
微應用被正確加載啦!(見下圖)
此時咱們打開控制檯,能夠看到咱們所執行的生命週期鉤子函數(見下圖)
到這裏,React
微應用就接入成功了!
Angular
微應用Angular
與 qiankun
目前的兼容性並不太好,接入 Angular
微應用須要必定的耐心與技巧。
對於選擇
Angular
技術棧的前端開發來講,對這類狀況應該得心應手(沒有辦法)。
咱們以 實戰案例 - feature-inject-sub-apps 分支 爲例,咱們在主應用的同級目錄(micro-app-main
同級目錄),使用 @angular/cli
先建立一個 Angular
的項目,在命令行運行以下命令:
ng new micro-app-angular
本文的 @angular/cli
選項以下圖所示,你也能夠根據本身的喜愛選擇配置。
而後,咱們建立幾個路由頁面再加上一些樣式,最後效果以下:
在建立好了 Angular
微應用後,咱們能夠開始咱們的接入工做了。首先咱們須要在主應用中註冊該微應用的信息,代碼實現以下:
// micro-app-main/src/micro/apps.ts const apps = [ /** * name: 微應用名稱 - 具備惟一性 * entry: 微應用入口 - 經過該地址加載微應用 * container: 微應用掛載節點 - 微應用加載完成後將掛載在該節點上 * activeRule: 微應用觸發的路由規則 - 觸發路由規則後將加載該微應用 */ { name: "AngularMicroApp", entry: "//localhost:10300", container: "#frame", activeRule: "/angular", }, ]; export default apps;
經過上面的代碼,咱們就在主應用中註冊了咱們的 Angular
微應用,進入 /angular
路由時將加載咱們的 Angular
微應用。
咱們在菜單配置處也加入 Angular
微應用的快捷入口,代碼實現以下:
// micro-app-main/src/App.vue //... export default class App extends Vue { /** * 菜單列表 * key: 惟一 Key 值 * title: 菜單標題 * path: 菜單對應的路徑 */ menus = [ { key: "Home", title: "主頁", path: "/", }, { key: "AngularMicroApp", title: "Angular 主頁", path: "/angular", }, { key: "AngularMicroAppList", title: "Angular 列表頁", path: "/angular/list", }, ]; }
菜單配置完成後,咱們的主應用基座效果圖以下
最後咱們在主應用的入口文件,引入 zone.js
,代碼實現以下:
Angular
運行依賴於zone.js
。
qiankun
基於single-spa
實現,single-spa
明確指出一個項目的zone.js
只能存在一份實例,因此咱們在主應用注入zone.js
。
// micro-app-main/src/main.js // 爲 Angular 微應用所作的 zone 包注入 import "zone.js/dist/zone";
在主應用的工做完成後,咱們還須要對微應用進行一系列的配置。首先,咱們使用 single-spa-angular
生成一套配置,在命令行運行如下命令:
# 安裝 single-spa yarn add single-spa -S # 添加 single-spa-angular ng add single-spa-angular
運行命令時,根據本身的需求選擇配置便可,本文配置以下:
在生成 single-spa
配置後,咱們須要進行一些 qiankun
的接入配置。咱們在 Angular
微應用的入口文件 main.single-spa.ts
中,導出 qiankun
主應用所須要的三個生命週期鉤子函數,代碼實現以下:
從上圖來分析:
第 21 行
:微應用獨立運行時,直接執行掛載函數掛載微應用。第 46 行
:微應用導出的生命週期鉤子函數 - bootstrap
。第 50 行
:微應用導出的生命週期鉤子函數 - mount
。第 54 行
:微應用導出的生命週期鉤子函數 - unmount
。完整代碼實現以下:
// micro-app-angular/src/main.single-spa.ts import { enableProdMode, NgZone } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { Router } from "@angular/router"; import { ɵAnimationEngine as AnimationEngine } from "@angular/animations/browser"; import { singleSpaAngular, getSingleSpaExtraProviders, } from "single-spa-angular"; import { AppModule } from "./app/app.module"; import { environment } from "./environments/environment"; import { singleSpaPropsSubject } from "./single-spa/single-spa-props"; if (environment.production) { enableProdMode(); } // 微應用單獨啓動時運行 if (!(window as any).__POWERED_BY_QIANKUN__) { platformBrowserDynamic() .bootstrapModule(AppModule) .catch((err) => console.error(err)); } const { bootstrap, mount, unmount } = singleSpaAngular({ bootstrapFunction: (singleSpaProps) => { singleSpaPropsSubject.next(singleSpaProps); return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule( AppModule ); }, template: "<app-root />", Router, NgZone, AnimationEngine, }); /** 主應用生命週期鉤子中運行 */ export { /** * bootstrap 只會在微應用初始化的時候調用一次,下次微應用從新進入時會直接調用 mount 鉤子,不會再重複觸發 bootstrap。 * 一般咱們能夠在這裏作一些全局變量的初始化,好比不會在 unmount 階段被銷燬的應用級別的緩存等。 */ bootstrap, /** * 應用每次進入都會調用 mount 方法,一般咱們在這裏觸發應用的渲染方法 */ mount, /** * 應用每次 切出/卸載 會調用的方法,一般在這裏咱們會卸載微應用的應用實例 */ unmount, };
在配置好了入口文件 main.single-spa.ts
後,咱們還須要配置 webpack
,使 main.single-spa.ts
導出的生命週期鉤子函數能夠被 qiankun
識別獲取。
咱們直接配置 extra-webpack.config.js
便可,代碼實現以下:
// micro-app-angular/extra-webpack.config.js const singleSpaAngularWebpack = require("single-spa-angular/lib/webpack") .default; const webpackMerge = require("webpack-merge"); module.exports = (angularWebpackConfig, options) => { const singleSpaWebpackConfig = singleSpaAngularWebpack( angularWebpackConfig, options ); const singleSpaConfig = { output: { // 微應用的包名,這裏與主應用中註冊的微應用名稱一致 library: "AngularMicroApp", // 將你的 library 暴露爲全部的模塊定義下均可運行的方式 libraryTarget: "umd", }, }; const mergedConfig = webpackMerge.smart( singleSpaWebpackConfig, singleSpaConfig ); return mergedConfig; };
咱們須要重點關注一下 output
選項,當咱們把 libraryTarget
設置爲 umd
後,咱們的 library
就暴露爲全部的模塊定義下均可運行的方式了,主應用就能夠獲取到微應用的生命週期鉤子函數了。
在 extra-webpack.config.js
修改完成後,咱們還須要修改一下 package.json
中的啓動命令,修改以下:
// micro-app-angular/package.json { //... "script": { //... // --disable-host-check: 關閉主機檢查,使微應用能夠被 fetch // --port: 監聽端口 // --base-href: 站點的起始路徑,與主應用中配置的一致 "start": "ng serve --disable-host-check --port 10300 --base-href /angular" } }
修改完成後,咱們從新啓動 Angular
微應用,而後打開主應用基座 http://localhost:9999
。咱們點擊左側菜單切換到微應用,此時咱們的 Angular
微應用被正確加載啦!(見下圖)
到這裏,Angular
微應用就接入成功了!
Jquery、xxx...
微應用這裏的Jquery、xxx...
微應用指的是沒有使用腳手架,直接採用html + css + js
三劍客開發的應用。本案例使用了一些高級
ES
語法,請使用谷歌瀏覽器運行查看效果。
咱們以 實戰案例 - feature-inject-sub-apps 分支 爲例,咱們在主應用的同級目錄(micro-app-main
同級目錄),手動建立目錄 micro-app-static
。
咱們使用 express
做爲服務器加載靜態 html
,咱們先編輯 package.json
,設置啓動命令和相關依賴。
// micro-app-static/package.json { "name": "micro-app-jquery", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "cors": "^2.8.5" }, "devDependencies": { "nodemon": "^2.0.2" } }
而後添加入口文件 index.js
,代碼實現以下:
// micro-app-static/index.js const express = require("express"); const cors = require("cors"); const app = express(); // 解決跨域問題 app.use(cors()); app.use('/', express.static('static')); // 監聽端口 app.listen(10400, () => { console.log("server is listening in http://localhost:10400") });
使用 npm install
安裝相關依賴後,咱們使用 npm start
啓動應用。
咱們新建 static
文件夾,在文件夾內新增一個靜態頁面 index.html
(代碼在後面會貼出),加上一些樣式後,打開瀏覽器,最後效果以下:
在建立好了 Static
微應用後,咱們能夠開始咱們的接入工做了。首先咱們須要在主應用中註冊該微應用的信息,代碼實現以下:
// micro-app-main/src/micro/apps.ts const apps = [ /** * name: 微應用名稱 - 具備惟一性 * entry: 微應用入口 - 經過該地址加載微應用 * container: 微應用掛載節點 - 微應用加載完成後將掛載在該節點上 * activeRule: 微應用觸發的路由規則 - 觸發路由規則後將加載該微應用 */ { name: "StaticMicroApp", entry: "//localhost:10400", container: "#frame", activeRule: "/static" }, ]; export default apps;
經過上面的代碼,咱們就在主應用中註冊了咱們的 Static
微應用,進入 /static
路由時將加載咱們的 Static
微應用。
咱們在菜單配置處也加入 Static
微應用的快捷入口,代碼實現以下:
// micro-app-main/src/App.vue //... export default class App extends Vue { /** * 菜單列表 * key: 惟一 Key 值 * title: 菜單標題 * path: 菜單對應的路徑 */ menus = [ { key: "Home", title: "主頁", path: "/" }, { key: "StaticMicroApp", title: "Static 微應用", path: "/static" } ]; }
菜單配置完成後,咱們的主應用基座效果圖以下
在主應用註冊好了微應用後,咱們還須要直接寫微應用 index.html
的代碼便可,代碼實現以下:
從上圖來分析:
第 70 行
:微應用的掛載函數,在主應用中運行時將在 mount
生命週期鉤子函數中調用,能夠保證在沙箱內運行。第 77 行
:微應用獨立運行時,直接執行 render
函數掛載微應用。第 88 行
:微應用註冊的生命週期鉤子函數 - bootstrap
。第 95 行
:微應用註冊的生命週期鉤子函數 - mount
。第 102 行
:微應用註冊的生命週期鉤子函數 - unmount
。完整代碼實現以下:
<!-- micro-app-static/static/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <!-- 引入 bootstrap --> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" /> <title>Jquery App</title> </head> <body> <section id="jquery-app-container" style="padding: 20px; color: blue;" ></section> </body> <!-- 引入 jquery --> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> /** * 請求接口數據,構建 HTML */ async function buildHTML() { const result = await fetch("http://dev-api.jt-gmall.com/mall", { method: "POST", headers: { "Content-Type": "application/json", }, // graphql 的查詢風格 body: JSON.stringify({ query: `{ vegetableList (page: 1, pageSize: 20) { page, pageSize, total, items { _id, name, poster, price } } }`, }), }).then((res) => res.json()); const list = result.data.vegetableList.items; const html = `<table class="table"> <thead> <tr> <th scope="col">菜名</th> <th scope="col">圖片</th> <th scope="col">報價</th> </tr> </thead> <tbody> ${list .map( (item) => ` <tr> <td> <img style="width: 40px; height: 40px; border-radius: 100%;" src="${item.poster}"></img> </td> <td>${item.name}</td> <td>¥ ${item.price}</td> </tr> ` ) .join("")} </tbody> </table>`; return html; } /** * 渲染函數 * 兩種狀況:主應用生命週期鉤子中運行 / 微應用單獨啓動時運行 */ const render = async ($) => { const html = await buildHTML(); $("#jquery-app-container").html(html); return Promise.resolve(); }; // 獨立運行時,直接掛載應用 if (!window.__POWERED_BY_QIANKUN__) { render($); } ((global) => { /** * 註冊微應用生命週期鉤子函數 * global[appName] 中的 appName 與主應用中註冊的微應用名稱一致 */ global["StaticMicroApp"] = { /** * bootstrap 只會在微應用初始化的時候調用一次,下次微應用從新進入時會直接調用 mount 鉤子,不會再重複觸發 bootstrap。 * 一般咱們能夠在這裏作一些全局變量的初始化,好比不會在 unmount 階段被銷燬的應用級別的緩存等。 */ bootstrap: () => { console.log("MicroJqueryApp bootstraped"); return Promise.resolve(); }, /** * 應用每次進入都會調用 mount 方法,一般咱們在這裏觸發應用的渲染方法 */ mount: () => { console.log("MicroJqueryApp mount"); return render($); }, /** * 應用每次 切出/卸載 會調用的方法,一般在這裏咱們會卸載微應用的應用實例 */ unmount: () => { console.log("MicroJqueryApp unmount"); return Promise.resolve(); }, }; })(window); </script> </html>
在構建好了 Static
微應用後,咱們打開主應用基座 http://localhost:9999
。咱們點擊左側菜單切換到微應用,此時能夠看到,咱們的 Static
微應用被正確加載啦!(見下圖)
此時咱們打開控制檯,能夠看到咱們所執行的生命週期鉤子函數(見下圖)
到這裏,Static
微應用就接入成功了!
若是在 Static
微應用的 html
中注入 SPA
路由功能的話,將演變成單頁應用,只須要在主應用中註冊一次。
若是是多個 html
的多頁應用 - MPA
,則須要在服務器(或反向代理服務器)中經過 referer
頭返回對應的 html
文件,或者在主應用中註冊多個微應用(不推薦)。
最後,咱們全部微應用都註冊在主應用和主應用的菜單中,效果圖以下:
從上圖能夠看出,咱們把不一樣技術棧 Vue、React、Angular、Jquery...
的微應用都已經接入到主應用基座中啦!
若是您已經看到這裏了,但願您仍是點個 贊
再走吧~
您的 點贊
是對做者的最大鼓勵,也可讓更多人看到本篇文章!
若是感興趣的話,請關注 博客 或者關注做者便可獲取最新動態!