跳到主要内容

微前端

大型的前端应用拆分成多个模块,每个前端模块可以有不同的前端团队管理,并可以自主选择框架,独立部署上线,

多用于:中后台项目

为什么升级微服务架构

5w一下:单体架构

几十万-几百万:单体架构部署在多台服务器(nginx负载)

几百w 几千万: 负载均衡 + 主从数据库(运营比较高

微服务架构: 把单体架构项目抽离成多个项目,部署到多台服务器

好处

  • 技术栈无关 主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立运行、独立部署 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

    在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

微前端的原理

  1. 独立部署: 每个微前端都是独立的,可以单独构建、测试和部署。
  2. 独立运行: 微前端在运行时是独立的,它们可以并行加载,互不干扰。
  3. 通信机制: 微前端之间需要有通信机制来协调它们的行为。这可以通过发布/订阅模式、事件总线、Web Components等方式实现。
  4. 共享依赖: 为了减少重复代码和提高效率,微前端可以共享一些通用的依赖,如样式库、工具库等。
  5. 路由管理: 微前端需要一种机制来管理路由,确保用户在不同微前端之间切换时,能够正确加载对应的微前端。

Monorepo

  1. 代码共享: 在同一个仓库中,不同项目或模块可以轻松共享代码和资源。
  2. 统一的依赖管理: 所有项目或模块共享同一个 package.json,使得依赖管理更加简单和一致。
  3. 统一的构建和测试: 可以使用统一的构建和测试流程,确保所有项目或模块的质量和一致性。
  4. 版本控制: 由于所有项目或模块都在同一个仓库中,版本控制变得更加简单和直观。

微前端与 Monorepo 的结合

  • 通过将所有微前端放在同一个仓库中,可以简化依赖管理和版本控制,同时也可以方便地共享通用代码和资源。
  • 可以帮助维护一个统一的构建和测试流程,确保微前端的质量和一致性。

框架

  1. Iframe:
    • 缺点
      • url不同步,刷新页面,iframe页面路由丢失
      • 全局上下问完全隔离,内存遍历不共享
      • ui不同步
      • 慢,每次子应用进入都是需要资源重载
  2. Single-SPA:
    • 支持多种前端框架,如 React、Vue.js、Angular 等。
    • 对比iframe优点:
      • 子应用之间共享全局上下文,不存在url不同步和ui不同步的情况。
    • 缺点:
      • 没有实现js隔离和css隔离
      • 需要修改大量的配置,包括基座和子应用,不能开箱就用
  3. Qiankun:
    • 由蚂蚁金服开源的一个微前端框架。
    • 基于 single-spa 并提供了更加开箱即用。
    • HTML entry方式介入像iframe一样简单
    • 在浏览器空闲时间预加载未打开的微前端资源,加速应用打开速度
    • 支持 React、Vue.js、Angular 等主流前端框架。
    • 缺点:基座和子应用之间的样式隔离还没有实现,还会出现覆盖和冲突的问题
      • 使用固定的格式
      • Css-module
  4. Garfish:(加菲鱼)
    • 由字节跳动开源的一个微前端框架。
    • 提供了应用间通信、样式隔离、插件化等功能。
    • 支持 React、Vue.js、Angular、Ember 等前端框架。
  5. MicroApp:
    • 由京东开源的一个微前端框架。
    • 它在 基座应用子应用 之间充当桥梁胶水的作用。
    • 专注于应用之间的通信和协作,提供了丰富的事件和数据共享 API。
    • 支持主流前端框架,并且提供了可视化的开发和调试工具。

micro 和qiankun优缺点区别

micro-app 它通过 Web Components 技术实现子应用的隔离和通信。

优点

  • 体积小,加载速度快,对主应用的性能影响较小。
  • 配置简单,易于上手,适合快速开发。
  • 兼容性好,支持主流浏览器。

缺点

  • 功能相对有限:功能较少,可能需要额外的插件或自定义代码来实现一些高级功能。

qiankun 是基于 Single-SPA 的微前端框架。

优点

  • 功能丰富:提供了丰富的功能,如样式隔离、预加载、全局状态管理等。
  • 强大的样式隔离:通过 CSS 沙箱技术,可以较好地隔离子应用的样式,避免样式冲突。
  • 成熟的社区和文档qiankun 有成熟的社区支持和详细的文档,便于开发者学习和使用。

缺点

  • 性能开销:由于功能丰富,qiankun 的性能开销相对较大,可能会影响主应用的性能。
  • 配置复杂度:配置相对复杂,需要一定的学习成本。

qiankun的全局状态管理

initGlobalState方法实现。这个方法允许主应用初始化一个全局状态,并返回一个MicroAppStateActions实例,该实例包含三个方法

  • onGlobalStateChange: 用于监听全局状态的变化。当全局状态发生变化时,会触发回调函数,回调函数的参数包括新的状态和旧的状态。
  • setGlobalState: 用于设置全局状态。在子应用中,只能修改已存在的一级属性。
  • offGlobalStateChange: 用于移除当前应用的状态监听。当子应用卸载时,会自动调用此方法。

qiankun的start函数的作用和参数

  • 用于启动微前端应用的主要函数。
  • 负责初始化主应用和子应用的加载、挂载和卸载等生命周期管理。
  • 可以将主应用和子应用整合到一起,形成一个完整的微前端应用。
  1. 应用注册start 函数允许主应用注册子应用,子应用可以是单页面应用(SPA)或传统的多页面应用(MPA)。
  2. 生命周期管理:它负责管理子应用的生命周期,包括加载(bootstrap)、挂载(mount)、卸载(unmount)等。
  3. 路由管理start 函数还负责处理应用间的路由跳转,确保子应用能够正确响应主应用的路由变化。
  4. 样式隔离:它通过沙箱机制隔离子应用的样式,防止样式冲突。
  5. 全局状态管理start 函数可以集成全局状态管理,使得主应用和子应用可以共享和管理状态

属性:

  • singular: 布尔值,用于指定是否为单实例模式。如果设置为 true,则同一时间只允许一个子应用被激活。默认值为 false
  • fetch: 一个函数,用于自定义子应用的资源加载方式。默认情况下,qiankun 使用 window.fetch 来加载子应用的资源。
  • sandbox: 一个对象,用于配置子应用的沙箱环境。可以指定沙箱的类型,如 strictno-communication 等。
  • getTemplate: 一个函数,用于自定义子应用的挂载点模板。默认情况下,qiankun 使用一个简单的 div 作为挂载点。
  • props: 一个对象,用于向子应用传递额外的属性。这些属性可以在子应用中通过 props 参数接收。

qiankun如何处理js沙箱不能解决的js污染问题

js沙箱通过代理window对象来实现的,可以有效隔离子应用的全局变量,防止子应用之间的全局变量污染。如果使用onclick或addEventListener给<body>添加了一个点击事件

micro-app

CustomElement结合自定义的ShadowDom,将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染。

并且由于自定义ShadowDom的隔离特性,micro-app不需要像single-spaqiankun一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改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>
)
}

生命周期列表

1. created

<micro-app>标签初始化后,加载资源前触发。

2. beforemount

加载资源完成后,开始渲染之前触发。

3. mounted

子应用渲染结束后触发。

4. unmount

子应用卸载时触发。

5. error

子应用渲染出错时触发,只有会导致渲染终止的错误才会触发此生命周期。

数据通信

  • 子应用获取来自基座应用的数据
    • 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 的隔离。

  1. JavaScript 隔离

Qiankun 提供了两种沙箱模式:

  • 快照沙箱(SnapshotSandbox):适用于不支持 Proxy 的浏览器。通过保存和恢复全局对象(如 window)的快照来实现隔离。
  • Proxy 沙箱(LegacySandbox 和 ProxySandbox):利用 ES6 的 Proxy 对象,拦截对 window 的访问和修改,确保每个子应用有自己的独立环境。

Proxy 沙箱的工作原理

  • 创建一个假的 window 对象(通过 Proxy 代理)。
  • 当子应用访问或修改 window 上的属性时,操作会被代理到子应用自己的沙箱环境中。
  • 子应用卸载时,沙箱会清理子应用的全局变量和事件监听,避免污染主应用或其他子应用。
  1. CSS 隔离

Scoped CSS

  • 子应用的样式表在加载时会被动态添加到 DOM 中,卸载时会被移除。
  • 使用 scoped 属性或 CSS Modules 等技术,确保样式只作用于当前子应用的 DOM 元素。

Shadow DOM

将子应用渲染到 Shadow DOM 中,利用 Shadow DOM 的天然隔离特性,确保不会泄漏到其他应用中。

动态添加/移除样式表

在子应用加载时,会动态插入子应用的样式表;在子应用卸载时,会移除这些样式表。

  1. 其他隔离机制

除了 JavaScript 和 CSS 隔离,Qiankun 还通过以下方式确保子应用之间的独立性:

  • 全局事件隔离:通过重写 addEventListenerremoveEventListener,确保子应用的事件监听不会影响其他应用。
  • 定时器隔离:通过劫持 setTimeoutsetInterval,确保子应用的定时器在卸载时被清理。

qiankun的通信方式

主应用与子应用之间的通信(跨应用通信)

  1. 通过 props 传递数据(嵌套式通信): Qiankun 允许将一些数据通过 props 传递给子应用。例如,主应用可以将一些配置或数据传递给子应用,子应用可以在挂载时接收这些数据并处理。

    // 主应用传递数据给子应用
    const app = {
    name: 'app-vue',
    entry: 'http://localhost:7100',
    props: {
    customData: 'Hello, Sub-App',
    }
    };
  2. 通过 qiankun 的全局事件机制(事件通信): Qiankun 提供了 setGlobalStateonGlobalStateChange 这样的 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 子应用与子应用之间的通信

  1. setGlobalStateonGlobalStateChange: 子应用之间也可以通过 setGlobalStateonGlobalStateChange 进行通信。通过这种方式,子应用可以相互共享数据和事件。
    • 子应用 A 更新全局状态,子应用 B 会收到状态变化的通知。
    • 这种方式适用于子应用间有一些共享数据或者需要同步的场景。
  2. 自定义事件机制: 子应用之间还可以通过自定义事件(CustomEvent)来传递消息。例如,子应用 A 可以触发一个事件,子应用 B 可以监听该事件并作出响应。

Qiankun 与 Iframe 的区别

  • Qiankun 主要用于 微前端架构,通过共享状态、事件、props 等方式在主应用与子应用之间进行通信,子应用可以是任意前端框架(如 React、Vue 等),并且通常运行在同一个 JavaScript 环境中。
  • Iframe 是浏览器中嵌套另一个页面的方式,每个 Iframe 都是一个独立的浏览器上下文,主应用和 Iframe 之间的通信通常通过 postMessage 或其他方式进行。