微前端
大型的前端应用拆分成多个模块,每个前端模块可以有不同的前端团队管理,并可以自主选择框架,独立部署上线,
多用于:中后台项目
为什么升级微服务架构
5w一下:单体架构
几十万-几百万:单体架构部署在多台服务器(nginx负载)
几百w 几千万: 负载均衡 + 主从数据库(运营比较高
微服务架构: 把单体架构项目抽离成多个项目,部署到多台服务器
好处
-
技术栈无关 主框架不限制接入应用的技术栈,微应用具备完全自主权
-
独立开发、独立运行、独立部署 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
-
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
微前端的原理
- 独立部署: 每个微前端都是独立的,可以单独构建、测试和部署。
- 独立运行: 微前端在运行时是独立的,它们可以并行加载,互不干扰。
- 通信机制: 微前端之间需要有通信机制来协调它们的行为。这可以通过发布/订阅模式、事件总线、Web Components等方式实现。
- 共享依赖: 为了减少重复代码和提高效率,微前端可以共享一些通用的依赖,如样式库、工具库等。
- 路由管理: 微前端需要一种机制来管理路由,确保用户在不同微前端之间切换时,能够正确加载对应的微前端。
Monorepo
- 代码共享: 在同一个仓库中,不同项目或模块可以轻松共享代码和资源。
- 统一的依赖管理: 所有项目或模块共享同一个
package.json
,使得依赖管理更加简单和一致。 - 统一的构建和测试: 可以使用统一的构建和测试流程,确保所有项目或模块的质量和一致性。
- 版本控制: 由于所有项目或模块都在同一个仓库中,版本控制变得更加简单和直观。
微前端与 Monorepo 的结合
- 通过将所有微前端放在同一个仓库中,可以简化依赖管理和版本控制,同时也可以方便地共享通用代码和资源。
- 可以帮助维护一个统一的构建和测试流程,确保微前端的质量和一致性。
框架
- Iframe:
- 缺点
- url不同步,刷新页面,iframe页面路由丢失
- 全局上下问完全隔离,内存遍历不共享
- ui不同步
- 慢,每次子应用进入都是需要资源重载
- 缺点
- Single-SPA:
- 支持多种前端框架,如 React、Vue.js、Angular 等。
- 对比iframe优点:
- 子应用之间共享全局上下文,不存在url不同步和ui不同步的情况。
- 缺点:
- 没有实现js隔离和css隔离
- 需要修改大量的配置,包括基座和子应用,不能开箱就用
- Qiankun:
- 由蚂蚁金服开源的一个微前端框架。
- 基于 single-spa 并提供了更加开箱即用。
- HTML entry方式介入像iframe一样简单
- 在浏览器空闲时间预加载未打开的微前端资源,加速应用打开速度
- 支持 React、Vue.js、Angular 等主流前端框架。
- 缺点:基座和子应用之间的样式隔离还没有实现,还会出现覆盖和冲突的问题
- 使用固定的格式
- Css-module
- Garfish:(加菲鱼)
- 由字节跳动开源的一个微前端框架。
- 提供了应用间通信、样式隔离、插件化等功能。
- 支持 React、Vue.js、Angular、Ember 等前端框架。
- MicroApp:
- 由京东开源的一个微前端框架。
- 它在 基座应用 和 子应用 之间充当桥梁胶水的作用。
- 专注于应用之间的通信和协作,提供了丰富的事件和数据共享 API。
- 支持主流前端框架,并且提供了可视化的开发和调试工具。
micro 和qiankun优缺点区别
micro-app
它通过 Web Components 技术实现子应用的隔离和通信。
优点:
- 体积小,加载速度快,对主应用的性能影响较小。
- 配置简单,易于上手,适合快速开发。
- 兼容性好,支持主流浏览器。
缺点:
- 功能相对有限:功能较少,可能需要额外的插件或自定义代码来实现一些高级功能。
qiankun
是基于 Single-SPA 的微前端框架。
优点:
- 功能丰富:提供了丰富的功能,如样式隔离、预加载、全局状态管理等。
- 强大的样式隔离:通过 CSS 沙箱技术,可以较好地隔离子应用的样式,避免样式冲突。
- 成熟的社区和文档:
qiankun
有成熟的社区支持和详细的文档,便于开发者学习和使用。
缺点:
- 性能开销:由于功能丰富,
qiankun
的性能开销相对较大,可能会影响主应用的性能。 - 配置复杂度:配置相对复杂,需要一定的学习成本。
qiankun的全局状态管理
initGlobalState
方法实现。这个方法允许主应用初始化一个全局状态,并返回一个MicroAppStateActions
实例,该实例包含三个方法
- onGlobalStateChange: 用于监听全局状态的变化。当全局状态发生变化时,会触发回调函数,回调函数的参数包括新的状态和旧的状态。
- setGlobalState: 用于设置全局状态。在子应用中,只能修改已存在的一级属性。
- offGlobalStateChange: 用于移除当前应用的状态监听。当子应用卸载时,会自动调用此方法。
qiankun的start函数的作用和参数
- 用于启动微前端应用的主要函数。
- 负责初始化主应用和子应用的加载、挂载和卸载等生命周期管理。
- 可以将主应用和子应用整合到一起,形成一个完整的微前端应用。
- 应用注册:
start
函数允许主应用注册子应用,子应用可以是单页面应用(SPA)或传统的多页面应用(MPA)。 - 生命周期管理:它负责管理子应用的生命周期,包括加载(bootstrap)、挂载(mount)、卸载(unmount)等。
- 路由管理:
start
函数还负责处理应用间的路由跳转,确保子应用能够正确响应主应用的路由变化。 - 样式隔离:它通过沙箱机制隔离子应用的样式,防止样式冲突。
- 全局状态管理:
start
函数可以集成全局状态管理,使得主应用和子应用可以共享和管理状态
属性:
- singular: 布尔值,用于指定是否为单实例模式。如果设置为
true
,则同一时间只允许一个子应用被激活。默认值为false
。 - fetch: 一个函数,用于自定义子应用的资源加载方式。默认情况下,qiankun 使用
window.fetch
来加载子应用的资源。 - sandbox: 一个对象,用于配置子应用的沙箱环境。可以指定沙箱的类型,如
strict
、no-communication
等。 - getTemplate: 一个函数,用于自定义子应用的挂载点模板。默认情况下,qiankun 使用一个简单的 div 作为挂载点。
- props: 一个对象,用于向子应用传递额外的属性。这些属性可以在子应用中通过
props
参数接收。
qiankun如何处理js沙箱不能解决的js污染问题
js沙箱通过代理window
对象来实现的,可以有效隔离子应用的全局变量,防止子应用之间的全局变量污染。如果使用onclick或addEventListener给<body>
添加了一个点击事件
micro-app
CustomElement结合自定义的ShadowDom,将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染。
并且由于自定义ShadowDom的隔离特性,micro-app
不需要像single-spa
和qiankun
一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack配置,是目前市面上接入微前端成本最低的方案。
// my-page.js
export function MyPage () {
return (
<div>
<h1>子应用</h1>
// name(必传):应用名称
// url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
// baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
<micro-app name='app1' url='http://localhost:3000/' baseroute='/my-page'></micro-app>
</div>
)
}
//设置基础路由(如果基座应用是history路由,子应用是hash路由,这一步可以省略)
// router.js
import { BrowserRouter, Switch, Route } from 'react-router-dom'
export default function AppRoute () {
return (
// 👇 设置基础路由,子应用可以通过window.__MICRO_APP_BASE_ROUTE__获取基座下发的baseroute,如果没有设置baseroute属性,则此值默认为空字符串
<BrowserRouter basename={window.__MICRO_APP_BASE_ROUTE__ || '/'}>
...
</BrowserRouter>
)
}
生命周期列表
<micro-app>
标签初始化后,加载资源前触发。
加载资源完成后,开始渲染之前触发。
子应用渲染结束后触发。
子应用卸载时触发。
子应用渲染出错时触发,只有会导致渲染终止的错误才会触发此生命周期。
数据通信
- 子应用获取来自基座应用的数据
- const data = window.microApp.getData()
- window.microApp.addDataListener(dataListener: Function, autoTrigger?: boolean)
- window.microApp.removeDataListener(dataListener: Function)
- window.microApp.clearDataListener()
- 子应用向基座应用发送数据
window.microApp.dispatch({type: '子应用发送的数据'})
- 基座应用向子应用发送数据
- Micro-app 的data
- 手动发送数据
- microApp.setData
- 基座应用获取来自子应用的数据
- microApp.getData(appName)
- 全局数据通信
- 发送全局数据
microApp.setGlobalData({type: '全局数据'})
microApp.addGlobalDataListener(dataListener: Function, autoTrigger?: boolean)
// 解绑监听函数microApp.removeGlobalDataListener(dataListener: Function)
// 清空基座应用绑定的所有全局数据监听函数microApp.clearGlobalDataListener()
- 发送全局数据
微前端应用之间传
- 全局状态管理:使用 Redux、MobX 等库来维护全局状态,所有子应用共享此状态。
- 事件总线:创建一个简单的事件发布/订阅系统,子应用可以通过事件发送和接收参数。
- URL 参数:通过浏览器的 URL 查询参数来传递数据。
Qiankun 采用了以下机制来确保 JavaScript 和 CSS 的隔离。
- JavaScript 隔离
Qiankun 提供了两种沙箱模式:
- 快照沙箱(SnapshotSandbox):适用于不支持 Proxy 的浏览器。通过保存和恢复全局对象(如
window
)的快照来实现隔离。 - Proxy 沙箱(LegacySandbox 和 ProxySandbox):利用 ES6 的 Proxy 对象,拦截对
window
的访问和修改,确保每个子应用有自己的独立环境。
Proxy 沙箱的工作原理:
- 创建一个假的
window
对象(通过 Proxy 代理)。 - 当子应用访问或修改
window
上的属性时,操作会被代理到子应用自己的沙箱环境中。 - 子应用卸载时,沙箱会清理子应用的全局变量和事件监听,避免污染主应用或其他子应用。
- CSS 隔离
Scoped CSS
- 子应用的样式表在加载时会被动态添加到 DOM 中,卸载时会被移除。
- 使用
scoped
属性或 CSS Modules 等技术,确保样式只作用于当前子应用的 DOM 元素。
Shadow DOM
将子应用渲染到 Shadow DOM 中,利用 Shadow DOM 的天然隔离特性,确保不会泄漏到其他应用中。
动态添加/移除样式表
在子应用加载时,会动态插入子应用的样式表;在子应用卸载时,会移除这些样式表。
- 其他隔离机制
除了 JavaScript 和 CSS 隔离,Qiankun 还通过以下方式确保子应用之间的独立性:
- 全局事件隔离:通过重写
addEventListener
和removeEventListener
,确保子应用的事件监听不会影响其他应用。 - 定时器隔离:通过劫持
setTimeout
和setInterval
,确保子应用的定时器在卸载时被清理。
qiankun的通信方式
主应用与子应用之间的通信(跨应用通信)
-
通过 props 传递数据(嵌套式通信): Qiankun 允许将一些数据通过 props 传递给子应用。例如,主应用可以将一些配置或数据传递给子应用,子应用可以在挂载时接收这些数据并处理。
// 主应用传递数据给子应用
const app = {
name: 'app-vue',
entry: 'http://localhost:7100',
props: {
customData: 'Hello, Sub-App',
}
}; -
通过
qiankun
的全局事件机制(事件通信): Qiankun 提供了setGlobalState
和onGlobalStateChange
这样的 API,允许在不同的子应用之间共享全局状态或事件:setGlobalState(state)
: 用于更新全局状态。onGlobalStateChange(callback)
: 用于监听全局状态的变化。
// 在主应用中设置全局状态
window.__POWERED_BY_QIANKUN__ && window.setGlobalState({
user: 'John',
role: 'admin'
});
// 在子应用中监听全局状态
window.__POWERED_BY_QIANKUN__ && window.onGlobalStateChange((state, prevState) => {
console.log('Global state changed:', state);
});
1.2 子应用与子应用之间的通信
setGlobalState
和onGlobalStateChange
: 子应用之间也可以通过setGlobalState
和onGlobalStateChange
进行通信。通过这种方式,子应用可以相互共享数据和事件。- 子应用 A 更新全局状态,子应用 B 会收到状态变化的通知。
- 这种方式适用于子应用间有一些共享数据或者需要同步的场景。
- 自定义事件机制: 子应用之间还可以通过自定义事件(
CustomEvent
)来传递消息。例如,子应用 A 可以触发一个事件,子应用 B 可以监听该事件并作出响应。
Qiankun 与 Iframe 的区别
- Qiankun 主要用于 微前端架构,通过共享状态、事件、props 等方式在主应用与子应用之间进行通信,子应用可以是任意前端框架(如 React、Vue 等),并且通常运行在同一个 JavaScript 环境中。
- Iframe 是浏览器中嵌套另一个页面的方式,每个 Iframe 都是一个独立的浏览器上下文,主应用和 Iframe 之间的通信通常通过
postMessage
或其他方式进行。