手动实现微前端框架的思路

作者: 前端辉羽 | 来源:发表于2022-04-25 22:10 被阅读0次

    此文是本人学习慕课网微前端课程《从零打造微前端框架:实战“汽车资讯平台”项目》的笔记,主要记录了自研实现微前端框架的思路。

    • 一、构师思维塑造
      • 1.1.软件设计原则
      • 1.2.架构种类
      • 1.3.微前端实现方式
      • 1.4.概念上架构质量的衡量
      • 1.5.日常开发过程中的架构质量
      • 1.6.架构的前期准备
      • 1.7.技术债务的产生
      • 1.8.技术填补
      • 1.9.预防系统崩溃
      • 1.10.重构流程
    • 二、打造微前端框架
      • 2.1.微前端实现方式对比
      • 2.2.确定技术栈
      • 2.3.分析框架
      • 2.4.梳理好思路,绘制项目架构图
      • 2.5.自动启动所有子应用的脚本
      • 2.6.构建服务
      • 2.7.子应用接入微前端vue2
      • 2.8.主应用开发
      • 2.9.css样式隔离
      • 2.10.父子通信
      • 2.11.提高加载性能
    • 三、发布框架与自动部署应用
      • 3.1.通过npm发布框架
      • 3.2.应用部署-创建自动部署平台

    一、构师思维塑造

    1.1.软件设计原则

    1.单一职责原则:永远不应该有多于一个原因来改变某个类
    2.开放封闭原则:软件实体扩展应该是开放的,但是对于修改应该是封闭的
    3.里氏替换原则:父类一定是能够被子类替换
    4.最少知识原则:只与你最直接的对象交流(高内聚,低耦合,做系统设计时,尽量减少依赖关系)
    5.接口隔离原则:一个类与另一个类之间的依赖性,应该依赖于尽可能小的接口(不要对外暴露没有实际意义的接口,用户不应该依赖它不需要的接口)
    6.依赖倒置原则:高层模块不应该依赖底层模块,它们应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象(应该面向接口编程,不应该面向实现类编程)

    满足上边六大原则 设计出稳定的软件架构

    1.2.架构种类

    1.系统级架构,微前端可以算是系统级架构,也可以算是应用级架构
    2.应用级架构:脚手架,模式库(ui库),设计系统
    3.模块级架构:迭代
    4.代码级架构:规范与原则

    微前端是一个系统内应用间的架构方案
    在多个应用之间,微前端则是一种系统间等架构方案

    1.3.微前端实现方式

    单实例:同一时刻,只有一个子应用被展示,子应用具备一个完整的应用生命周期
    多实例:同一时刻,可以展示多个子应用,通常基于url的变化来做子应用的切换,通常使用web components方案来做子应用封装,子应用更像是一个业务组件而不是应用。

    系统的稳定性:当一个实际的系统处于一个平衡的状态时,如果受到外来作用的影响时,系统经过一个过渡过程仍然能够回到原来的平衡状态,我们称这个系统就是稳定,否则称系统不稳定
    1.架构设计的基石 2.可以更好的实现自我修复

    系统的健壮性:计算机软件在输入错误,磁盘故障,网络过载或有意攻击情况下,能否不死机,不崩溃,就是该软件健壮性的具体表现
    健壮性也可以称之为鲁棒性
    健壮性的度量标准:
    1.一个软件可以从错误的输入推断出正确合理的输入
    2.一个软件可以正确的运行在不同环境下
    3.一个软件能够检测自己内部的设计或者编码错误,并得到正确的结果

    1.4.概念上架构质量的衡量

    1.拓展性
    2.维护性
    3.可管理
    4.高可用(故障修复,容灾,降级,熔断)

    1.5.日常开发过程中的架构质量

    1.理解难度
    2.接入依赖的成本
    3.崩溃率和错误率的指标
    4.开发效率
    5.错误上报和信息收集等功能

    1.6.架构的前期准备

    架构师分类:
    系统架构师:从系统的维度,负责整体系统的架构设计,纯技术架构
    应用架构师:偏向业务的系统,关注理解业务
    业务架构师:从业务流程的维度,关注某一个行业,所做的事情可能会脱离具体的代码开发,偏向于数据分析

    1.7.技术债务的产生

    1.开发过程中因为时间紧迫而导致的实现不合理
    2.开发过程中暂时没有想到更好的办法而去妥协进行的实现
    3.架构设计前期没有考虑到的细节
    4.不合理的交互设计,导致技术实现复杂
    5.一些旧的功能在做的时候并没有详细的文档,并没有预留拓展接口,导致拓展困难,上线后问题剧增

    技术债务累积过多产生的可能后果:
    1.修复变重构,严重影响产出,
    2.对旧功能需要不断进行兼容处理,严重影响开发速度

    1.8.技术填补

    1.项目拆分,代码解耦
    2.必须有日志模块,操作日志,错误日志,业务日志等等
    3.技术培训和传帮带能力
    4.做好代码规范,编写好技术文档
    5.不同工程师相互code review
    6.用于发现系统中的技术债务

    等产品上线后,开发就没有那么紧啦,这个时间大家可以找个时间处理技术债务,一遍建立感情,一遍品味原来的代码

    1.9.预防系统崩溃

    系统崩溃属于严重的架构设计事故
    崩溃预防:
    1.抓取用户行为,获取用户操作链条
    2.解决村存量的技术债务问题
    3.减少新增问题
    4.对脏数据进行兜底和检验
    5.单元测试
    6.崩溃预警
    7.自动化测试
    8.更广的灰度触达
    9.性能优化体系

    当技术债务累积到一定的程度时,小软件可以考虑直接重构,快刀斩乱麻
    而随便软件系统越来越复杂,微前端架构则成为架构优化(微重构)的解决方案之一

    1.10.重构流程

    1.确定问题点,确定重构功能和范围
    2.旧架构设计和逻辑梳理
    3.稳定性保证
    4.性能保证
    5.需求过程中的冲突问题

    二、初识微前端

    2.1.微前端实现方式对比

    1.iframe
    优势:技术成熟,支持页面嵌入,天然支持运行沙箱隔离,独立运行
    劣势:页面之间的域名可以是不同的;需要设计相应的应用通讯机制,如何监听传参格式等内容;应用加载,渲染,缓存都是交给了浏览器处理,可控性低
    2.web component
    优势:支持自定义元素;支持shadow dom,并可通过关联进行控制;支持模板template和插槽slot,引入自定义组件内容
    劣势:接入微前端需要重写当前项目;生态系统不完善,容易出兼容性问题;整体架构设计复杂,组件之间通讯也控制繁琐
    3.自研框架
    优势:高度定制化,满足所兼容的一切场景;独立的通信机制和沙箱运行环境,可解决应用之间相互影响的问题;支持不同技术栈子应用,可无缝实现页面无刷新渲染
    劣势:技术实现难度较高;需要设计一套定制的通信机制;首次加载会出现资源过大的情况

    最终实现选择-自研框架
    1.路由分发式
    2.主应用控制路由匹配和子应用加载,共享依赖加载
    3.子应用做功能,并接入主应用实现主子控制和联动

    2.2.确定技术栈

    主应用:选定vue3技术栈
    子应用:
    vue2:实现新能源应用
    vue3:首页,选车
    react15:资讯,视频,视频详情
    react16:新车,排行,登录
    后端服务和发布应用
    服务端接口:koa实现
    发布应用:express
    使用原生nodejs去手写完成微前端框架

    2.3.分析框架

    主应用:1.注册子应用 2.加载和渲染子应用 3.路由匹配(activeWhen,rules 由框架判断) 4.获取数据(公共以来,通过数据做鉴权处理) 5.通信机制(父子通信,子父的通信)
    子应用:1.渲染自身功能 2.监听通信(主应用传递过来的数据,根据传递过来的数据做一些对应的更新操作)
    微前端框架:1.实现子应用的注册 2.开始内容(应用加载完成,开启微前端框架) 3.匹配对应的子应用,加载对应子应用的内容,完成所有依赖项的执行 4.将子应用渲染在固定的容器内 5.公共的事件管理 6.异常的捕获和报错 6.全局的状态管理 7.沙箱隔离,保证子应用运行期间不被其他子应用干扰 7.通信机制
    服务端的功能: 1.提供数据服务
    发布平台:1.主子应用的打包和发布

    2.4.梳理好思路,绘制项目架构图

    工具推荐:processon


    image.png

    2.5.自动启动所有子应用的脚本

    在根目录的package.json配置start命令
    4-7的内容:在微前端的根目录下配置一个脚本去同时运行一个指令,同时打开所有的应用

    2.6.构建服务

    构建一个后端服务:
    npm install koa-generator -g
    koa -V
    koa2 service

    快速生成一个koa2项目
    bin里的www是启动文件
    public 静态资源文件

    安装一个插件帮助修改node的时候自动重启node服务
    npm install supervisor --save-dev
    安装后start启动服务的命令中用supervisor 代替node

    koa解决跨域问题:
    npm install koa2-cors --save-dev
    app.js进行引入并使用const cors = require('koa2-cors')
    将服务设置为可跨域的状态

    get请求中ctx.request.query可以获取到前端接口路径携带的参数
    post请求 ctx.reuqest.body

    2.7.子应用接入微前端vue2

    首先需要做的:主应用获得子应用的生命周期,结构,方法,文件等,这样主才能控制子的渲染和加载

    Vue2子应用接入微前端
    子应用vue.config.js配置关键点devServer里的contentBase和headers允许跨域和output

    Vue2子应用本身创建实例new Vue(),在微前端框架中,这个实例的执行与销毁,都应该交给主应用去执行

    子应用需要改造的文件 1.vue.config.js 2.main.js

    react15接入微前端
    修改webpack.config.js里的output和devServer
    改造入口文件index.js

    webpack正常打包会将所有代码打包成一个立即执行函数

    (function(){
    })()
    

    配置了output的library之后就会打包成library的属性值比如demo

    const demo = (function(){})()
    

    就可以通国window.demo拿到当前自应用的所有内容

    2.8.主应用开发

    中央控制器-主应用 main项目,使用的vue3技术栈
    主应用在页面设置一个子应用的容器

    在主应用进行子应用注册
    设置一个navList记录子应用的注册信息store文件里的leftNav.js
    添加注册子应用的方法utils文件夹里的startMicroApp.js

    路由拦截,监听路由切换,重写路由切换事件,另外别忘了监听浏览器的前进/后退按钮window.onpopstate

    微前端框架-获取首个子应用
    进入项目,首个显示的子应用,先验证当前子应用列表是否为空,不为空的时候根据route匹配查找出当前的子应用,如果子应用存在,则跳转到子应用对应的url

    因为路由的跳转又结合了手动的路由跳转,为了避免多次触发自定义的路由跳转事件,给当前的子应用添加上标记到window对象上

    window.__CURRENT_SUB_APP__ = app.activeRule;
    
    if(window.__CURRENT_SUB_APP__ === window.location.pathname){
      return
    }
    

    通过主应用生命周期去对应控制子应用的内容显示
    beforeLoad开始加载
    mounted渲染完成
    destoryed卸载完成
    传递过去的主应用的生命周期先在微前端里进行保存,然后通过全局的loading来控制子应用的内容显示/loading

    微前端挂载到主应用,执行注册函数时,传入的就是主应用的生命周期,遍历执行这个主应用的所有生命周期,微前端框架就可以生成注册到主应用
    微前端的生命周期:如果路由变化时,对子应用进行对应的销毁和加载操作

    获取需要展示的页面-加载和解析html
    在主应用生命周期beforeLoad去获取页面内容
    子应用显示页面本质上是通过get请求(network中的doc)去获取页面信息,由此我们就可以在微前端框架中通过get请求去获取到子应用对应的页面,根据url通过fetch拿到res,然后res.text()拿到页面内容,然后将内容赋值给对应的容器就可以显示子应用对应的页面了,但是直接赋值给容器,容器是没法解析html中的标签,link和script(src,js代码)元素,需要专门提取出来这些元素进行处理

    微前端框架环境变量设置
    通过sandbox设置环境变量,运行js文件

    隔离运行环境-快照沙箱(记录当前执行的子应用的变量,当子应用切换的时候,将变量置为初始值)
    不同子应用之间的运行环境应该进行隔离,防止全局变量的互相污染
    for循环window,new map一个新对象作为快照对象,快照沙箱适合比较老的浏览器

    更现代化的沙箱方法:

    let a = {}
    const proxy = new Proxy(a , {
      get(){},
      set(){}
    })
    console.log(proxy.a) //此处会触发proxy的get
    proxy.a=1 //此处会触发proxy的set
    

    利用proxy去代理window,监听window的改变
    设置一个空对象defaultValue去储存子应用的变量,当window改变时,将改变值以key,value的形式存储到defaultValue中。当需要获取window属性值的时候,也在代理的get中去返回defaultValue对应的值,沙箱销毁的时候inactive也只需要将defaultValue置为{}

    2.9.css样式隔离

    1.css modules 利用webpack配置打包机制进行css模块化处理
    2.shadow dom 创建个新的元素进行包裹,但语法较新,兼容性较差
    3.minicss 一个webpack插件,将css单独打包成文件,然后每个页面通过link进行引用

    2.10.父子通信

    比如说动态控制主应用的header和footer的显示与隐藏
    1.props
    好莱坞原则-不用联系我,当我需要的时候会打电话给你
    依赖注入-主应用的显示隐藏,注入到子应用内部,通过子应用内部的方法进行调用
    2.customEvent 通过new CustomEvent对象去触发和监听事件

    子应用之间进行通信
    1.props 子应用1跟父应用交互,子应用2再跟父应用交互,拿到子应用1传递出来的信息
    2.customEvent

    全局状态管理-全局store
    利用发布订阅模式在微前端

    export const creatStore = (initData) => (() => {
      // 利用闭包去保存传参的初始数据
      let store = initData;
      // 管理所有的订阅者,依赖
      let observers = [];
      // 获取store
      const getStore = () => {
        return store;
      }
      // 更新store
      const updateStore = (newValue) => new Promise((res) => {
        if (newValue !== store) {
          // 执行保存store的操作
          let oldValue = store;
          // 将store更新
          store = newValue;
          res(store);
          // 通知所有的订阅者
          observers.forEach(fn => fn(newValue, oldValue));
        }
      })
      // 添加订阅者
      const subscribeStore = (fn) => {
        observers.push(fn);
      }
      // 把方法return出去
      return { getStore, updateStore, subscribeStore }
    })()
    //整个store本质是一个闭包函数
    

    2.11.提高加载性能

    提高加载性能-性能缓存
    定义一个cache对象,根据子应用的name来做缓存,如果当前的html已经解析并且加载过,就返回已经加载过的内容。如果没有,则走正常加载和解析的流程

    提高加载性能-预加载子应用
    1.获取到所有子应用列表,不包括当前正在显示的
    2.预加载剩下的所有的子应用

    三、发布框架与自动部署应用

    3.1.通过npm发布框架

    1.npm init初始化要发布的包
    2.前置条件:需要有一个自己的npm账号;在终端npm login登录上自己的npm账号;使用npm whoami查看当前登录的账号
    3.npm publish 进行发布 npm unpublish 取消发布
    1.0.0
    第一位:主版本号 第二位:次版本号 第三位:修订号
    npm version patch 使用命令行修改修订号
    npm version minor 次版本号
    npm version major 主版本号

    3.2.应用部署-创建自动部署平台

    创建一个自动发布的express项目platform文件夹
    新建一个index. html,点击按钮,遍历所有要发布的应用,然后分别触发ajax
    npm install express express-generator -g
    全局安装了上面的两个应用后,在命令行就可以使用express命令了
    express -e 生成服务代码
    文件路径和koa-generator生成的项目差不多
    app.js中设置允许跨域

    app.all('*', function (req, res, next) {
      res.header('Access-Control-Allow-Origin', '*');
      next();
    });
    

    npm start启动项目,routes中创建一个/start路由,这是实现各个应用自动打包和发布的关键路由

    相关文章

      网友评论

        本文标题:手动实现微前端框架的思路

        本文链接:https://www.haomeiwen.com/subject/hwtzjrtx.html