微前端
大型的前端应用拆分成多个模块,每个前端模块可以有不同的前端团队管理,并可以自主选择框架,独立部署上线,
多用于:中后台项目
为什么升级微服务架构
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或其他方式进行。