深入浅出微前端

355 阅读6分钟

微前端实现方式对比

优势

  • 技术栈无关
  • 主框架不限制介入应用的技术栈,微应用具备完全自主权
  • 独立开发、独立部署
  • 增量升级
  • 微前端是一种非常好的实现渐进式重构的手段和策略
  • 微应用仓库独立,前后端可独立开发,主框架自动完成同步更新
  • 独立运行
  • 每个微应用之间状态隔离,运行时状态不共享

劣势

  • 接入难度较高
  • 应用场景移动端少,管理端多

Iframe

优势

  • 技术成熟
  • 支持页面嵌入
  • 天然支持运行沙箱隔离,独立运行

劣势

  • 页面之间url不同步,浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  • 需要对应的设计一套应用通讯机制,如何监听、传参格式等内容
  • 应用加载、渲染、缓存等体系的实现

web component

优势

  • 支持自定义元素
  • 支持shadow dom,并可通过关联进行控制
  • 支持模板template和插槽slot,引入自定义组件内容

劣势

  • 接入微前端需要重写当前项目
  • 生态系统不完善,技术过新容易出现兼容性问题
  • 整体框架设计复杂,组件与组件之间拆分过细时,容易造成通讯和控制繁琐

自研框架

优势

  • 高度定制化,满足需要做兼容的一切场景
  • 独立的通信机制和沙箱运行环境,可解决应用之间相互影响的问题
  • 支持不同技术栈子应用,可无缝实现页面无刷新渲染

劣势

  • 技术实现难道较高
  • 需要设计一套定制的通信机制
  • 首页加载会出现资源过大情况

实现方式

  • 路由分发式
  • 主应用控制路由匹配和子应用加载,共享依赖加载
  • 子应用做功能,去接入主应用实现主子控制和联动

确定技术栈

  • 主应用
    • vue3(都行的其实)
  • 子应用
    • vue2 新能源业面
    • vue3 首页、选车
    • react15 资讯、视频、视频详情
    • react16 新车、排行、登录
  • 服务端接口
    • Koa实现
  • 发布平台
    • express

项目架构

主应用

  • 注册子应用
  • 加载、渲染子应用
  • 路由匹配(activeWhen、rules -由框架判断)
  • 获取数据(公共依赖、通过数据做鉴权处理)
  • 通信(父子通信、子父的通信)

子应用

  • 渲染
  • 监听通信(主应用传递过来的数据)

微前端

  • 子应用的注册
  • 有开始内容(应用加载完成)
  • 路由更新判断
  • 匹配对应的子应用
  • 加载子应用的内容
  • 完成所有依赖项的执行
  • 将子应用渲染在固定的容器内
  • 公共事件的管理
  • 异常的捕获和报错
  • 全局的状态管理的内容
  • 沙箱的隔离
  • 通信机制

服务端的功能

  • 提供数据服务

发布平台

  • 主子应用的打包和发布

小技巧

一次性启动所有子项目

  • 在主目录的package.json上配置
"scripts": {
    "start""node ./build/run.js"
  },
  • 在build文件夹下新建run.js文件
const childProcess = require('child_process')
const path = require('path')
const filePath = {
  vue2: path.join(__dirname, '../vue2'),
  vue3: path.join(__dirname, '../vue3'),
  react15: path.join(__dirname, '../react15'),
  react16: path.join(__dirname, '../react16'),
  main: path.join(__dirname, '../main')
}
// cd 子应用的目录 npm start 启动项目
function runChild () {
  Object.values(filePath).forEach(item => {
    // 执行的是shell命令,并在终端上显示出来
    childProcess.spawn(`cd ${item} && npm start`, { stdio"inherit"shelltrue })
  })
}
runChild()

构建一个后端服务

  • 安装koa生成器 npm install koa-generator -g
  • 生成 koa2 service
  • 安装service的依赖 npm i
  • 启动服务 npm run start
  • 在默认情况下,修改服务内容需要重启服务
    • 修改为文件改动时候自动重启服务,安装监听改动自启动插件 npm install supervisor --save-dev
  • 结构目录
    • bin/www 启动文件
    • public 静态目录
    • routes 请求的路径
    • app.js 入口文件

后端服务请求处理

  • 在routes中创建子应用对应的路由,如vue2.js,在里面配置请求路径

  • 在app.js中引入及使用

const vue2 = require('./routes/vue2')
app.use(vue2.routes(), vue2.allowedMethods())

跨域的解决

  • 使用中间件koa2-cors,在service目录下运行   npm install koa2-cors --save-dev
  • 在app.js中引入及使用
  const koa2Cors = require('koa2-cors')
  app.use(koa2Cors())

- 如何获取前端传的接口参数

  - get - ctx.request.query

  - post -  ctx.body = ctx.request.body

子应用接入微前端

vue2子应用改造

  • 配置vue.config.js
  • 配置main.js,暴露生命周期

vue3子应用改造(跟vue2类似)

  • 配置vue.config.js
  • 配置main.js,暴露生命周期

react15子应用改造

  • 配置webpack.config.js
  • 配置index.js,暴露生命周期

react16子应用改造(跟react16类似)

  • 配置webpack.config.js
  • 配置index.js,暴露生命周期

中央控制器-主应用开发

  • 主应用选用vue3(都行其实)
  • 主应用不需要改造
  • 没有vue.config.js

子应用注册

  • 在store的sub.js中配置子应用信息
{
    name'react15',// 唯一
    entry'//localhost:9002/',// 入口
    loading,
    container'#micro-container',// 容器-对应app.vue里的id
    activeRule'/react15',// 激活路由
    appInfo,
}
  • 在/micro/start.js编写注册方法注册子应用,在index.js中暴露出去
  • 在/src/util/index.js中编写注册逻辑、启动逻辑
  • 在main.js中引入后调用
import { subNavList } from './store/sub'
import { registerApp } from './util'
registerApp(subNavList)

微前端框架

路由拦截

  • 在start.js调用路由拦截方法
  • 路由拦截相关在/micro/router文件夹内

获取首个子应用

  • 在/micro/start.js编写启动方法,在index.js中暴露出去
  • 在/src/util/index.js中registerApp方法中调用
  • 在main.js中触发registerApp

主应用生命周期

  • 注册到微前端时传的第二个参数就是生命周期

微前端生命周期

  • 在lifeCycle文件夹下
  • beforeLoad
  • mounted
  • destoryed

获取需要展示的页面-加载和解析html

  • 相关文件micro/loader,在lifeCycle的beforeloader里调用

加载和解析js

  • 相关文件micro/loader里的getResources

执行js脚本

  • 相关文件sandbox/performScript.js

微前端框架开发 - 添加辅助功能

微前端环境变量设置

  • 相关文件sandbox/index.js

运行环境隔离

  • 运用时的变量子应用间隔离 快照沙箱
  • 相关文件sandbox/snapShotSandbox.js暴露类分别在子应用生命周期处理前和销毁时调用
  • 应用场景:比较老版本的浏览器
  • 性能消耗大,只支持单实例 代理沙箱
  • 相关文件sandbox/proxySandbox.js暴露类分别在子应用生命周期处理前和销毁时调用

css样式隔离

css modules

  • 在每个子应用的webpack.config.js中配置,如:
loader:'css-loader',
options:{
  module:true,
}

shadow dom

  • 使用id.attachShadow({mode:true})开启
  • 比较新的语法,兼容性低 mini css
  • 使用插件mini css
  • 将子应用的css打包成单独的css文件,然后通过link引用

应用间通信

父子通信

  • props
    • 主应用通过暴露api来操作其 vuex
    • 在sub.js中引入传递给subNavList的appInfo
    • 在lifeCycle的mount生命周期时从vuex中获取传入appinfo
    • 在子应用的mount中就能接收到appinfo,可以对主应用暴露的api进行相关操作和调用
  • customevent
    • 在框架start.js中进行监听
    • 把CustomEvent绑定到window下
    • 在子应用中就可以通过window.custom.emit('test',{a:1})触发
    • 监听一定要在触发前做

子应用间通信

  • props(子应用1 - 父应用交互 - 子应用2)
  • customevent(流行的做法)
    • 使用window.custom.on('test',data=>{console.log(data)})监听
    • 使用window.custom.emit('test',{a:1})触发
    • 先有监听,再有触发

全局状态管理

  • 通过const storeData = window.store.getStore() 添加订阅者
  • 通过window.store.update({...storeData,a:11})添加update操作

提高加载性能

  • 应用缓存
    • 根据应用名缓存
  • 预加载子应用
    • 获取到所有子应用列表 - 不包括当前正在显示的
    • 预加载剩下的所有子应用