微前端的應用

大廠基本在使用的

  • 美團 https://tech.meituan.com/2019/12/26/meituan-bifrost.html
  • 阿里 https://qiankun.umijs.org/zh/

基於經驗 自己的考慮(以 Vue 項目 爲例子)

前言: 雖然不想搞輪子 ,但是 要吹牛,不搞點輪子 也不好意思得

demo 項目 https://gitee.com/qlaiqyc_572/mins-web

1.路由加載

  • 異步加載 預加載 路由全局合併
  • 是否是子路由 ,是否加載完畢 ,加載(loading ,error ,success)
  • 路由緩存

目前兩種方案 ,
第一種:使用 system.js 對 子項目進行 加載umd 對象 然後addRouter
第二種:公用全局變量 進行註冊 保存,(通過xhr 請求JS)
在這裏插入圖片描述
在這裏插入圖片描述
第二種

Vue-Route https://router.vuejs.org/zh/api/#router-addroutes
注意 坑 ,刷新的情況

1. 使用 system.js 進行模塊加載
	 let { default: webApp } = await System.import(url) 

2. 使用 router.addRouter 進行路由加載

 	 router.addRouter(webApp.router)

3. 問題:怎麼處理 loading 狀態,怎麼更新路由設置 

   解決方法: 和我們經常遇見的 路由權限一樣 只需要 重置 路由 進行添加


    // 重置路由
    
	const createRouter = ()=>{
	  return new VueRouter({
	    mode: "history",
	    base: process.env.BASE_URL,
	    routes
	  });
	}
	
	const router = createRouter();
	
	router.Qclear =()=>{
	  router.matcher =createRouter().matcher;
	};

  


 //路由監聽  子項目處理的清空,subPro 是子項目 加載前的loading 模塊 ,類似與打開小程序下載資源前的的 loading 狀態
 	async routeEvent(router,subPro){
          let {subWebs} = this.data,$t = this;

          //更新路由
          const updateRoute = async (subWebs)=>{

            router.Qclear();//清空路由

            let newRoute = [];
            subWebs.forEach(curr=>{
              if(curr.load) {
                newRoute = newRoute.concat(curr.webApp.routes)
              }else{
                newRoute.push({  path: "/"+curr.path, name: curr.path,  component:subPro})
              }
            });
            router.addRoutes(newRoute,{replace:true});

          }

          //處理初始化路由
          await updateRoute(subWebs);

          //監聽路由變化 判斷 是否進行加載
          router.beforeEach((to, from, next) => {
            let {subWebs} = $t.data,index = -1;
            subWebs.forEach((curr,cindex)=>{
              if(curr.path == to.name)index = cindex;
            });

            next()

            if(index == -1){return}

            (async ()=>{
              let curr = subWebs[index];
              //判斷是否加載
              if(!curr.load){
              //延時 看loading 效果
                await new Promise(resolve => {
                  setTimeout(()=>{
                    resolve();
                  },3000)
                })
                //加載 -- 這裏 就有思考了,(服務端渲染的加載情況 ,是否引入第三方加載工具,System.js 還是自己寫)
                // - 服務端渲染的情況 是否是在服務端進行加載 /還是在客戶端進行loading 加載
                // - 暫做 在客戶端進行加載的情況 ,服務端 有空再搞

                let { System } = window;
                // eslint-disable-next-line no-unused-vars
                let { default: webApp } = await System.import(curr.form);

                curr.load = true;
                curr.webApp = webApp;
                subWebs[index] = curr;
                $t.data.subWebs = subWebs;

                await updateRoute(subWebs);
              }

              next(to.path);//當前的導航被中斷,然後進行一個新的導航
            })()
          })
        },

基本第一步 就完了 看看:展示狀態

圖一

圖二
圖三

緩存路由:是使用 Qcenter 種的全局變量進行 保存

  //配置 處理
        data: {
          //保存主路由的基本信息
          main: {},
          //子項目配置
          subWebs:[
              //load:===是否加載 webApp 異步路由保存在緩存裏面
            {path:'web',desc:"子項目-01",form:'/web/main.js',port:"8081",load:false,webApp:{}}
          ]
        },

待優化:

  • 某些模塊 可以預先加載 ,減少 loading時間
  • 這樣寫 每次更新路由的時候都會進行 重置 ,暫時沒有想到更好的方式
  • 子項目 加載loading 可以配置 不同的狀態,類似骨架屏
  • 對於SSR項目處理 沒有進行單獨配置

小結總結:基本功能雖然實現了,但是仍舊需要 多考慮 多想想, 項目的配置 怎麼才能更簡單,怎麼才能更高效

2.數據同步

  • 子項目 和主項目 數據關聯關係
  • 子項目 之間的 數據關聯關係

代碼世界:有句話,很好,😂約定大於配置😂 -----簡單而言 就是 遵循 某些規範 儘量少BUG
原則上: 各個模塊大部分數據都是獨立的 ,很少是需要公用的(常見的 token, 登陸用戶信息,配置信息)

Vuex 的 store.registerModule 註冊子模塊的所有model

所有子項目 的模塊,都註冊到主項目上面 ,進行統一處理 命名規則 爲 子項目名稱

主項目 獲取子項目 種的值 (有未加載的情況 需要進行判斷)
在這裏插入圖片描述
子項目獲取 主項目的 值

  1. 問題:由於子項目的store 註冊到 主項目,在子項目 種獲取值 就有區別,原因是公用了 store
     
     解: 約定 ,在子項目 devClient 開發時 主動創建根目錄 ,取值的時候 都在 創建的目錄下,打包時 和 dev 時 去掉 根目錄,然後 進行取值(沒辦法 ,暫時想不到其他方法了)

     注: 子項目 store 的取值問題需要約定

3.變量隔離 (數據變量,樣式變量)

  1. 基本約定 (那些變量 不能 使用 設置關鍵字 關鍵詞 列入 windows 中綁定 相同變量)
  2. 咋處理呢,qiankun 的做法是 用哪個加載哪個,不用了就卸載掉,這樣就完成了隔離
  • 數據隔離:由於數據基本都在 Vuex store 全局使用一個主 Store 所以 ,基本沒啥問題
  • 樣式隔離::開發規範(如BEM)、CSS 預處理(如SASS)、模塊定義(如CSS Module)、用 JS 來寫(CSS-in-JS)、以及shadow DOM特性
  • 參考 http://www.ayqy.net/blog/micro-frontends/

4.事件消息

  • 子路由 之間的消息處理
  • 子路由與 主路由消息 處理

由於項目的都是基於 Vue 的所以使用 EventBus 就可以了,全局公用一個BUS,都是走公共方法裏面提取的哈(前提是)

 1. 創建文件 bus.js
 	import Vue from 'vue';  
	export default new Vue(); 
	
 2. 引用js 綁定事件 觸發事件
    import Bus from '~/commons/bus.js';
	
	//綁定事件
	Bus.$on('showTips', target => {  
        ...
        console.log(target);
        ...  
   	 });  
   	 
   	//觸發事件 
	Bus.$emit('showTips', data);   
  	

5.公共 方法的 提取暴露

  • 是個問題 主要是本地開發 和 本地協同開發的區別
  • 那些是公共的 東西(Bus,request, memory) 打包合併 js
  • 本地開發:遠程引入JS ()

6. 環境配置

之前就很多地方都說了 不同環境,再在這裏統一說一下

子項目

 package.json
 
  "scripts": {
    "dev": "vue-cli-service serve --mode development",// 本地協同 開發環境
    "devClient": "vue-cli-service serve --mode devClient",// 本地 單獨開發環境
    "build": "vue-cli-service build --mode production"
    "lib": "vue-cli-service build --target lib --name index src/main.js"  //正式打包
  },

7. SSR 項目關聯 (以後在說吧,沒有想好)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章