微前端与qiankun框架

4,942 阅读6分钟

微前端与qiankun框架

微前端概述

微前端(Micro Frontends)是一种新兴的前端架构模式,它将大型的前端应用程序拆分为多个独立的、可管理的小型应用或服务。这种方法允许不同的团队使用不同的技术栈开发和维护各自的部分,同时还能将它们无缝地集成到一个统一的用户界面中。

为什么选择微前端

在我们的场景中,PDM系统(基于Vue)需要调用标签自动化系统(基于React)的Excalidraw画板编辑功能。由于技术栈的差异,传统方法难以实现两个项目的融合。微前端架构为我们提供了一种优雅的解决方案,使得不同技术栈的应用可以像搭积木一样组合在一起。

微前端的优势

  1. 技术栈无关:允许在同一个项目中使用不同的前端框架。
  2. 独立开发部署:各个微应用可以独立开发、测试和部署。
  3. 增量升级:可以逐步升级旧系统,降低风险。
  4. 团队自治:不同团队可以负责不同的微应用,提高开发效率。

微前端实现方案比较

1. iframe方案

优点
  • 实现简单,是最古老且稳定的集成方式
  • 天然的JS沙箱隔离
缺点
  1. URL不同步,影响浏览器的前进/后退功能
  2. UI不同步,难以实现跨iframe的UI交互
  3. 全局上下文隔离,跨应用通信较困难
  4. 性能较差,每次都需要重新加载资源

2. 使用script或fetch动态加载HTML

这种方法灵活性较高,但需要自行处理很多集成问题,如样式隔离、JS沙箱等。

3. qiankun框架(推荐)

qiankun是阿里巴巴开源的一个基于single-spa的微前端实现库,它提供了更完善的功能和更好的开发体验。

核心架构

qiankun 基于 Single-SPA 框架构建,采用了 JS 沙箱 + 样式隔离 + 预加载 的技术方案。

主要技术原理

1. 应用注册与路由匹配
// 应用注册机制
registerMicroApps([
  {
    name: 'reactApp',
    entry: '//localhost:3000',
    container: '#container',
    activeRule: '/app-react',
  }
])

原理

  • 通过 activeRule 配置路由匹配规则
  • 监听浏览器路由变化(popstate、pushstate、replacestate)
  • 当路由匹配时,触发对应应用的加载和渲染
2. 应用加载机制

HTML Entry 方案

// 获取子应用的 HTML 内容
const html = await fetch(entry).then(res => res.text())
// 解析 HTML,提取 JS、CSS 资源
const { scripts, styles } = parseHTML(html)

原理

  • 通过 fetch 获取子应用的 HTML 文件
  • 解析 HTML,提取 <script><link> 标签
  • 动态创建 <script> 标签加载 JS 资源
  • 动态创建 <link> 标签加载 CSS 资源
3. JS 沙箱隔离

Proxy 沙箱

class ProxySandbox {
  constructor() {
    const rawWindow = window
    const fakeWindow = Object.create(null)
    
    const proxy = new Proxy(fakeWindow, {
      get(target, key) {
        // 优先从 fakeWindow 获取,否则从真实 window 获取
        return target[key] || rawWindow[key]
      },
      set(target, key, value) {
        target[key] = value
        return true
      }
    })
    
    this.proxy = proxy
  }
}

原理

  • 使用 Proxy 代理 window 对象
  • 子应用的全局变量存储在代理对象中
  • 避免子应用污染主应用的全局环境
  • 支持多实例沙箱(每个子应用独立沙箱)
4. 样式隔离

动态样式表

// 动态添加样式
const styleElement = document.createElement('style')
styleElement.textContent = cssContent
document.head.appendChild(styleElement)

// 应用卸载时移除样式
const removeStyle = () => {
  document.head.removeChild(styleElement)
}

原理

  • 子应用的 CSS 通过动态 <style> 标签注入
  • 应用卸载时自动移除对应的样式
  • 支持 CSS 前缀隔离(通过 postcss 插件)
5. 生命周期管理

应用生命周期

// 子应用需要导出的生命周期
export async function bootstrap() {
  // 应用初始化
}

export async function mount(props) {
  // 应用挂载
}

export async function unmount() {
  // 应用卸载
}

原理

  • 主应用通过 import() 动态加载子应用
  • 调用子应用导出的生命周期函数
  • 确保应用的正确加载、挂载和卸载
6. 通信机制

全局状态管理

// 主应用设置全局状态
setGlobalState({ user: 'admin' })

// 子应用监听状态变化
onGlobalStateChange((state, prev) => {
  console.log('状态变化', state, prev)
})

原理

  • 基于发布订阅模式
  • 主应用维护全局状态
  • 子应用可以订阅和修改全局状态
7. 预加载机制
// 预加载配置
prefetchApps([
  { name: 'reactApp', entry: '//localhost:3000' }
])

原理

  • 在空闲时间预加载子应用资源
  • 提升用户访问子应用时的加载速度
  • 支持多种预加载策略(all、async、manual)
优点
  1. 技术栈无关
  2. HTML Entry接入方式,让接入微应用像使用iframe一样简单
  3. 样式隔离,确保微应用之间样式不会互相影响
  4. JS沙箱,提供安全的运行环境
  5. 资源预加载,优化性能
  6. umi插件支持,简化接入流程
缺点
  • 有一定的学习成本

qiankun框架接入指南

主应用配置

  1. 安装qiankun

    yarn add qiankun # 或 npm i qiankun -S
    
  2. 注册微应用

    import { registerMicroApps, start } from 'qiankun';
    
    registerMicroApps([
      {
        name: 'reactApp',
        entry: '//localhost:3000',
        container: '#container',
        activeRule: '/app-react',
      },
      // ... 其他微应用
    ]);
    
    start();
    
  3. 手动加载微应用(可选)

    import { loadMicroApp } from 'qiankun';
    
    loadMicroApp({
      name: 'react',
      entry: '//localhost:8000',
      container: '#react',
    });
    
注意事项
  1. 子应用路由需要和主应用保持一致
  2. 主应用 hash 路由, 子应用也适用 hash 路由

子应用配置(以umi为例)

  1. 安装umi的qiankun插件

    yarn add @umijs/plugin-qiankun -D
    
  2. 修改.umirc.ts配置文件

    export default defineConfig({
      // hash 路由需要开启
      // history: {
      //   type: 'hash',
      // },
      qiankun: {
        slave: {},
      },
    });
    
  3. app.ts中导出生命周期钩子

    export const qiankun = {
      // 应用加载之前
      async bootstrap(props) {
        console.log('react bootstrap', props)
      },
      // 应用 render 之前触发
      async mount(props) {
       console.log('react mount', props)
       props.onGlobalStateChange((state, prev) => {
         // state: 变更后的状态; prev 变更前的状态
         console.log(state, prev)
       })
    
       props.setGlobalState({ key: 'react 222' })
     },
     // 应用卸载之后触发
     async unmount(props) {
       console.log('react unmount', props)
     },
     /**
      * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
      */
      async update(props) {
        console.log('react update', props)
      },
    }
    

注意事项

  1. 确保子应用的路由与主应用保持一致
  2. 如果主应用使用hash路由,子应用也应使用hash路由
  3. 处理好应用间的通信机制
  4. 注意样式隔离和JS沙箱的使用

结论

微前端架构,特别是使用qiankun框架,为我们提供了一种强大的方式来集成不同技术栈的应用。虽然有一定的学习和配置成本,但它带来的灵活性和可维护性是值得的。在我们的PDM系统与标签自动化系统集成的场景中,qiankun将是一个理想的选择。

参考资料

  1. qiankun官方文档
  2. umi插件-qiankun
  3. 微前端的核心价值