qiankun + vue + element 微前端實踐

qiankun + vue + element 的微前端架構項目,主項目與子應用均使用vue。支持三大前端框架可根據本身需求調整。css

微前端 qiankun

微前端是什麼、爲何要作微前端、qiankun是什麼這些筆者將再也不敘述,在前端微服務話提出的兩年裏已經有過了不少次的討論和「定義」。
qiankun有興趣的能夠搜一下。
暫時還對這方面未有過了解的同窗-> 傳送門:多是你見過最完善的微前端解決方案, qiankunhtml

簡單使用教程

鑑於qiankun文檔只有寥寥十幾行,這裏作一個簡單的概述(搬運)。
話很少說上步驟及代碼:前端

  1. 建立一個主項目工程目錄
  2. npm install qiankun
  3. 改造主項目入口文件:
    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>
  4. 建立一個子項目工程目錄並改造子應用
    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__;
    }
  5. 須要注意的是,咱們對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;
  6. 通過上述改造,一個簡易的微前端環境就草草建成了,是否是很簡單,你是否是已經躍躍欲試了?

父子應用通訊

在上述所建微前端應用中,父子間的通訊是極其廣泛且沒法繞過的需求,而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();

一個簡單的基於qiankun和vue的示例就這麼結束啦

固然咱們須要考慮的還有不少,可是我前天剛買的狼叔的【前端架構:從入門到微前端】告訴咱們,架構是一件持續性和漸進式的事兒,其餘的後續再說吧~~~
另附Github上的demo地址:wl-qiankun。不想看我在這羅裏吧嗦的直接代碼跑起吧~,若是你以爲還有一點點能夠,就請留個star吧~~

相關文章
相關標籤/搜索