微前端的应用

我心目中的微前端

  • 大厂基本在使用的
  • 基于经验 自己的考虑(以 Vue 项目 为例子)
    • 1.路由加载
    • 2.数据同步
    • 3.变量隔离 (数据变量,样式变量)
    • 4.事件消息
    • 5.公共 方法的 提取暴露
    • 6. 环境配置
    • 7. SSR 项目关联 (以后在说吧,没有想好)

大厂基本在使用的

  • 美团 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)
微前端的应用_第1张图片
微前端的应用_第2张图片
微前端的应用_第3张图片

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);//当前的导航被中断,然后进行一个新的导航
            })()
          })
        },

基本第一步 就完了 看看:展示状态

微前端的应用_第4张图片

微前端的应用_第5张图片
微前端的应用_第6张图片

缓存路由:是使用 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

所有子项目 的模块,都注册到主项目上面 ,进行统一处理 命名规则 为 子项目名称

主项目 获取子项目 种的值 (有未加载的情况 需要进行判断)
微前端的应用_第7张图片
子项目获取 主项目的 值

  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 项目关联 (以后在说吧,没有想好)

你可能感兴趣的:(web,思想之路)