介绍
微前端是一种类似于微服务的架构,是一种由多个独立交付的前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是一个完整的应用;或者将一些原本独立运行的应用,没有关联的应用糅合成一个整体。
它主要解决如下问题:
- 将大型项目拆分 便于维护
- 便于跨团队,跨部门合作
- 技术栈无关&业务无关 React,Vue 不同团队可以选择适合自己的技术栈
- 子应用只需要关系业务细节 诸如鉴权,登陆之类的交给主应用来做
现有的几种方案
Iframe
- 由于iframe自身的特性,完全隔离了css和js,不会造成不同子应用css或js的污染,同时子应用接入的成本较低,不需要做太大的改造。
- 缺点也很明显
- 由于完全隔离,父子应用通信需要通过postMessage进行(仅能传递字符串)
- 对于需要登陆的场景,需要思考如何将主应用的cookie透传到根域名不同的各个子应用中
- 浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用
- 如何收集子应用异常
- 对于子应用(不居中)的一个弹框(居中)在主应用达不到预期效果
single-spa & Garfish & qiankun
介绍
single-spa是通过监听 url change 事件,在路由变化时匹配到渲染的子应用并进行渲染,同时single-spa要求子应用修改渲染逻辑并暴露出三个方法: bootstrap 、 mount 、 unmount ,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。
因为qiankun是基于single-spa进行封装,所以这些特点也被qiankun继承下来,并且需要对webpack配置进行一些修改,并扩展了沙箱隔离,预加载,缓存,路由,插件等能力。
Garfish在qiankun的基础上做了一下优化,如沙箱隔离使用new Function替换Eval。
但目前来看spa方案qiankun使用的人数较多,社区活跃度较高,且方案更为成熟。
主应用
主应用中注册子应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import React, { useEffect } from 'react'; import Garfish from 'garfish';
Garfish.run({ basename: '/', domGetter: '#container', apps: [ { name: 'react', activeWhen: '/react', entry: 'http://localhost:3000', }, { name: 'vue', activeWhen: '/vue', entry: 'http://localhost:8080/index.js', }, ], });
const App = () => { useEffect(async () => { const app = await Garfish.loadApp('app1', { entry: 'http://localhost:3001', basename: '/', domGetter: '#container', props: { msg: 'hello world', }, }); await app.mount(); });
return ( <div className='main'> <h1>Main App</h1> <div id="container"></div> </div> ); };
export default App;
|
子应用
1 2 3 4 5 6 7 8 9
| export default defineConfig({ base: 'http://localhost:3000/', server: { port: 3000, cors: true, origin: 'http://localhost:3000', }, });
|
机制
- 生命周期
- Mount
- 创建app容器添加到dom上
- 编译子应用的代码
- 拿到子应用的provider
- 调用app.options.beforeMount钩子
- 调用provider.render
- 将app set到Garfish.activeApps中
- 调用app.options.afterMount钩子
- 如果渲染失败app.mount会返回false
- Unmount
- 调用app.options.beforeUnmount钩子
- 调用provider.destroy
- 移除app的dom
- app.display,app.mounted = false
- 将app从Garfish.activeApps中delete
- 调用 app.options.afterUnmount 钩子
- 根据app.unmount判断是否卸载成功
- Show
- app的容器添加到dom上
- 调用provider.render
- app.dispaly = true
- app.show判断是否渲染
- Hide
- 调用provider.destory
- 将app从dom移除
- app.dispaly = false
- app.hide判断是否隐藏成功
- 沙箱机制
- 不使用iframe隔离的情况下保证样式,全局变量不冲突
- Eval
- New Function eval需要每次都执行 new Function是重复执行一个函数 因此选用New Function
隔离方案
- 快照沙箱
在某个阶段给当前运行环境打上快照,需要的时候恢复快照。(运行时保存window所有的变量 卸载后把window缺失的变量存起来 等下一次调用恢复)
- vm沙箱
- 防止逃逸
- 路由机制
micro-app
介绍
借鉴了WebComponent的思想,通过CustomElement结合自定义的 ShadowDom ,将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染。并且由于自定义ShadowDom的隔离特性,micro-app不需要像single-spa和qiankun一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack配置, 相对接入微前端成本较低 。


主应用
1 2 3 4 5 6 7 8 9 10 11 12
| import ReactDOM from 'react-dom'; import './index.css'; import Router from './router'; import microApp from '@micro-zoe/micro-app'
microApp.start()
ReactDOM.render( <Router />, document.getElementById('root') );
|
为子应用添加路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { Suspense } from 'react' import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom' import Home from './pages/home/home' import config from './config' function App() { return ( <BrowserRouter basename='/main-react17'> <div id='router-container'> <Switch> <Route path="/" exact> <Home /> </Route> <Route path="/app-vue3_hxh"> <Suspense fallback={<div>Loading...</div>}> <micro-app name='appname-vue3' url={`${config.vue3}/child/vue3/`} baseroute='/main-react17/app-vue3_hxh' ></micro-app> </Suspense> </Route> <Redirect to='/' /> </Switch> </div> </BrowserRouter> ) }
export default App
|
子应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function mount () { history = createWebHistory(window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL) router = createRouter({ history, routes, }) as Router
app = createApp(App) app.use(router) app.mount('#vue3-app')
console.log('微应用child-vue3渲染了') }
|
webpack配置
1 2 3 4 5 6
| devServer: { headers: {'Access-Control-Allow-Origin': '*'} }
Nginx代理
|
生命周期等其他概念与qiankun or Garfish类似。
Module Federation
WIP……