qiankun + vue + element 的微前端架構項目,主項目與子應用均使用vue。支持三大前端框架可根據本身需求調整。css
微前端是什麼、爲何要作微前端、qiankun是什麼這些筆者將再也不敘述,在前端微服務話提出的兩年裏已經有過了不少次的討論和「定義」。
qiankun有興趣的能夠搜一下。
暫時還對這方面未有過了解的同窗-> 傳送門:多是你見過最完善的微前端解決方案, qiankunhtml
鑑於qiankun文檔只有寥寥十幾行,這裏作一個簡單的概述(搬運)。
話很少說上步驟及代碼:前端
改造主項目入口文件:
main.jsvue
// 導入qiankun依賴 import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from "qiankun"; function render({ appContent, loading }) { if (!app) { app = new Vue({ el: "#container", router, store, data() { return { content: appContent, loading }; }, render(h) { return h(App, { props: { content: this.content, loading: this.loading } }); } }); } else { app.content = appContent; app.loading = loading; } }; function genActiveRule(routerPrefix) { return location => location.pathname.startsWith(routerPrefix); } render({loading: true}); // 註冊子應用 registerMicroApps( [{ name: "app1" entry: "//localhost:7771", render, activeRule: genActiveRule("/app1") props: 'mg' // 傳遞給子應用 }], { beforeLoad: [ app => { console.log("before load", app); } ], beforeMount: [ app => { console.log("before mount", app); } ], afterUnmount: [ app => { console.log("after unload", app); } ] } ) // 設置默認子應用 setDefaultMountApp("/app1"); // 第一個子應用加載完畢回調 runAfterFirstMounted(); // 啓動微服務 start(); // 注意, 主應用的el綁定dom爲#container,所以你也須要修改一下index.hrml模板中的id
app.vue 增長一個渲染子應用的盒子react
<template> <div id="root" class="main-container"> <div class="main-container-menu"> </div> <div id="root-view" class="app-view-box" v- html="content"></div> </div> </template> <script> export default { name: "root-view", props: { loading: Boolean, content: String } }; </script>
建立一個子項目工程目錄並改造子應用
vue.comfig.jswebpack
const path = require("path"); const packageName = require("./package.json").name; function resolve(dir) { return path.join(__dirname, dir); } const port = 7771; // dev port module.exports = { outputDir: "dist", assetsDir: "static", // 默認在生成的靜態資源文件名中包含hash以控制緩存 filenameHashing: true, lintOnSave: false, devServer: { hot: true, disableHostCheck: true, port, overlay: { warnings: false, errors: true }, headers: { "Access-Control-Allow-Origin": "*" } }, // 自定義webpack配置 configureWebpack: { resolve: { alias: { "@": resolve("src") } }, output: { //把子應用打包成 umd 庫格式 library: `${packageName}-[name]`, libraryTarget: "umd", jsonpFunction: `webpackJsonp_${packageName}` } } };
main.jsgit
import Vue from "vue"; import VueRouter from "vue-router"; import App from "./App.vue"; import "./public-path"; import routes from "./router"; import store from "./store"; import "./plugins/element.js"; import "@/assets/css/demo.min.css" Vue.config.productionTip = false; let router = null; let instance = null; // 導出子應用生命週期到父應用 export async function bootstrap(props) { console.log(props) } export async function mount() { router = new VueRouter({ base: window.__POWERED_BY_QIANKUN__ ? "/app1" : "/", mode: "history", routes }); instance = new Vue({ router, store, render: h => h(App) }).$mount("#app"); } export async function unmount() { instance.$destroy(); instance = null; router = null; } // 子應用單獨開發環境 window.__POWERED_BY_QIANKUN__ || mount();
眼尖的同窗可能看到了咱們在main.js引入了一個public-path,其餘的你們都眼熟那麼這個public-path是什麼東西?
main.js同級增長public-path.js。或者你喜歡的其餘地方。github
// 僅僅配置下公共路徑 if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }
須要注意的是,咱們對vue的router.js導出作了一點小小的改動。(你也能夠不改動,在main.js也就無需router = null)。web
// 咱們將導出的實例化以後的router改成只導出了路由數據 const routes = [ { path: "/", name: "home", component: Home }, { 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/About.vue") } ]; /* const router = new VueRouter({ mode: "history", routes }); */ export default routes;
在上述所建微前端應用中,父子間的通訊是極其廣泛且沒法繞過的需求,而qiankun在這方面固然有所考慮。
在上述構建項目步驟中,有一步是在主應用main.js註冊子應用:vue-router
registerMicroApps( [{ name: "app1" entry: "//localhost:7771", render, activeRule: genActiveRule("/app1") props: 'mg' // 傳遞給子應用 }], )
其中props參數即爲傳遞給子應用的數據,其內容你能夠自由定製。
子應用在main.js導出的生命週期函數中都可接收到所傳props數據:
export async function bootstrap(props) { console.log(props) }
其props的應用相似於react框架的父子組件通訊,傳入data數據供自組件使用,傳入fn函數給子組件觸發向上回調。
按照這個思路咱們將主應用的main.js和子應用的main.js都改造一番:
改造後的主應用main.js
... // 定義傳入子應用的數據 let msg = { data: { auth: false }, fns: [ { name: "LOGOUT_", LOGOUT_(data) { alert('父應用返回信息:' + data) } } ] }; // 註冊子應用 registerMicroApps( [{ name: "app1" entry: "//localhost:7771", render, activeRule: genActiveRule("/app1") props: msg // 將定義好的數據傳遞給子應用 // 注意:一般這裏會經過獲取後臺數據異步傳入,具體再也不細說 }], { beforeLoad: [ app => { console.log("before load", app); } ], beforeMount: [ app => { console.log("before mount", app); } ], afterUnmount: [ app => { console.log("after unload", app); } ] } ) // 設置默認子應用 setDefaultMountApp("/app1"); // 第一個子應用加載完畢回調 runAfterFirstMounted(); // 啓動微服務 start();
改造後的子應用main.js
... let router = null; let instance = null; // 導出子應用生命週期到父應用 export async function bootstrap(props = {}) { // 將主應用傳遞過來的函數掛在vue原型方面全局使用 // 你也能夠在mount中接收掛在methods或者直接設定傳入mixin,壞處即是框架耦合度太大不符合微前端思想 Array.isArray(props.fns) && props.fns.map(i => { Vue.prototype[i.name] = i[i.name] }); } export async function mount() { router = new VueRouter({ base: window.__POWERED_BY_QIANKUN__ ? "/app1" : "/", mode: "history", routes }); instance = new Vue({ router, store, render: h => h(App) }).$mount("#app"); } export async function unmount() { instance.$destroy(); instance = null; router = null; } // 子應用單獨開發環境 window.__POWERED_BY_QIANKUN__ || mount();
固然咱們須要考慮的還有不少,可是我前天剛買的狼叔的【前端架構:從入門到微前端】告訴咱們,架構是一件持續性和漸進式的事兒,其餘的後續再說吧~~~
另附Github上的demo地址:wl-qiankun。不想看我在這羅裏吧嗦的直接代碼跑起吧~,若是你以爲還有一點點能夠,就請留個star吧~~