微前端实现方式对比
优势
- 技术栈无关
- 主框架不限制介入应用的技术栈,微应用具备完全自主权
- 独立开发、独立部署
- 增量升级
- 微前端是一种非常好的实现渐进式重构的手段和策略
- 微应用仓库独立,前后端可独立开发,主框架自动完成同步更新
- 独立运行
- 每个微应用之间状态隔离,运行时状态不共享
劣势
- 接入难度较高
- 应用场景移动端少,管理端多
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", shell: true })
})
}
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操作
提高加载性能
- 应用缓存
- 根据应用名缓存
- 预加载子应用
- 获取到所有子应用列表 - 不包括当前正在显示的
- 预加载剩下的所有子应用