深入理解 Qiankun:原理、落地与实战优化

77 阅读5分钟

摘要

  • Qiankun 是基于 single-spa 封装的微前端框架,解决子应用的加载/卸载、样式与 JS 隔离、应用通信等问题,适合多团队、大型项目的平滑拆分。
  • 核心实现要点:HTML Entry(import-html-entry)解析资源并注入;JS 沙箱(Proxy / 快照)避免全局污染;样式隔离(Shadow DOM / 运行时重写)保证 CSS 不互相影响。
  • 落地建议:用 prefetchApps 做智能预加载,基座统一 external 公共依赖或使用 Module Federation 做共享,Vite 集成需用专门插件。

一、为什么要选 Qiankun?(场景与价值)

微前端不是为“技术玩具”而生,而是为以下工程问题服务:

  • 大型前端单体仓库导致 多人协作冲突、构建链臃肿
  • 需要对老项目做 渐进式重构/迭代(不同技术栈并存)。
  • 要实现 子应用独立部署/灰度/回滚,降低整体风险。

Qiankun 把这些问题工程化:子应用独立构建,基座负责调度与聚合。其成熟生态、丰富 API 和生命周期使其成为企业级微前端的首选之一。


二、核心机制拆解(带底层实现思路)

1) HTML Entry:把子应用当“页面”来加载

Qiankun 的加载入口不是一个 JS 文件拼接,而是抓取子应用的 index.html,解析其中的 <link><script> 等资源,再动态注入到基座页面中。这个工作由 import-html-entry 完成。

优点:子应用能像独立页面那样工作;
缺点:需要处理跨域、资源基址等问题。


2) JS 沙箱:如何保证全局不污染?

  • Proxy 沙箱:对 window 的读写通过 Proxy 重定向到子应用私有对象,避免污染宿主全局。
  • 快照沙箱:进入子应用前保存全局状态,退出后恢复。

两者结合,既保证兼容性,也兼顾性能。


3) 样式隔离:Shadow DOM 与运行时重写

  • Strict 样式隔离:基于 Shadow DOM,浏览器原生隔离。
  • 运行时重写(experimentalStyleIsolation):运行时给 CSS 选择器加作用域前缀。

4) 生命周期:bootstrap / mount / unmount / update

子应用需暴露一组生命周期函数,基座在适当时机调用。这个设计借鉴 single-spa,并扩展了通信能力(props、initGlobalState 等)。


三、实战:Vite + Qiankun(最小可运行模版)

基座(主应用)示例

// base/src/main.tsx
import { registerMicroApps, start, prefetchApps, initGlobalState } from 'qiankun';
import { createRoot } from 'react-dom/client';
import App from './App';

registerMicroApps([
  {
    name: 'sub-vite-app',
    entry: 'http://localhost:7300',
    container: '#subapp-container',
    activeRule: '/sub-vite',
  },
]);

prefetchApps([{ name: 'sub-vite-app', entry: 'http://localhost:7300' }]);

const state = initGlobalState({ user: null });

start();
createRoot(document.getElementById('root')!).render(<App />);

(基座 HTML 里,如果子应用做了 external 化,需要注入 React/ReactDOM 的 CDN UMD 脚本。)

Vite 子应用(最小入口)示例

// sub-vite-app/src/main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

let root: ReturnType<typeof createRoot> | null = null;

function render(container?: HTMLElement) {
  const el = container ? container.querySelector('#root') : document.getElementById('root');
  root = createRoot(el!);
  root.render(<App />);
}

if (!(window as any).__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {}
export async function mount(props: any) {
  render(props.container);
}
export async function unmount() {
  root?.unmount();
}

Vite 子应用打包配置(external 化 React 示例)

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  base: './',
  build: {
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: { react: 'React', 'react-dom': 'ReactDOM' },
        format: 'umd' // 根据接入方式,可选 iife/umd;若保留 ESM,需要搭配特定 plugin
      }
    }
  },
  server: { port: 7300, cors: true }
});

提示:如果你不想改动很多 build 配置,使用 vite-plugin-qiankun 可以简化接入流程。

四、性能优化与资源共享(实战策略)

1) 预加载(prefetch / prefetchApps)

基座可以提前拉取常访问的子应用资源,缩短用户点击后的等待时间。prefetchApps 是 qiankun 提供的开箱即用策略,也可以配合流量埋点做智能预加载(按用户习惯预加载)

// 例:只预加载高频访问子应用
prefetchApps([
  { name: 'sub-vite-app', entry: 'http://localhost:7300' }
]);

2) 依赖 external 化(或 Module Federation)

  • External 化:在子应用 build 时把 React/Vue 等框架标为外部依赖,由基座统一通过 CDN/基座打包注入。好处是节省重复下载、减小子应用包体。风险是需要强制保证版本一致。npm
  • Module Federation(Webpack 5) :更灵活的共享机制,支持运行时共享与版本协商,适合统一使用 webpack 的团队。若项目里同时使用 webpack 与 Rspack,Module Federation 是优先级较高的共享方案。

3) 静态资源缓存与 CDN

  • 子应用静态资源放 CDN,配合合理 Cache-Control;对经常访问的资源可以设置更长 cache 或者使用 service worker 做离线缓存。

五、常见坑、调试与落地建议

常见坑与解决

  1. 资源路径与跨域:子应用 ESM 模式下资源请求的 origin 可能会被基座劫持,导致静态资源 404。解决:给子应用正确的 base,或让 import-html-entry 能正确处理资源基址。Stack Overflow
  2. 样式隔离未生效strictStyleIsolationexperimentalStyleIsolation 有差别,部分插件/版本下有已知问题,务必在目标浏览器与业务场景下充分验证。qiankun.umijs.org+1
  3. Vite HMR 与基座冲突:本地开发时子应用 dev server 端口、CORS、HMR 配置需要手动调优或使用社区插件以保证调试体验。npmGitHub
  4. 多版本 React/Vue 报错(尤其 hooks 报错) :务必统一 runtime 依赖版本或做严格隔离(external / Module Federation)。webpack

调试技巧

  • 在子应用的 mount/unmount 中加入日志与指标上报,验证加载/卸载是否执行并且资源被释放。
  • 使用浏览器 DevTools 的全局搜索来确认 window 下的全局变量是否被正确隔离或还原。
  • 在基座开启 addGlobalUncaughtErrorHandler 捕获和定位子应用错误。

六、参考资料(延伸阅读)

  • Qiankun 官方网站与文档。qiankun.umijs.org
  • Qiankun GitHub 仓库(实现、Issue、PR 可看源码与设计演进)。GitHub
  • import-html-entry(解析 HTML Entry 的实现包)。npm
  • Qiankun 教程(prefetch、生命周期、路由选择等)。qiankun.umijs.org
  • Vite 社区的 vite-plugin-qiankun / 兼容插件(调试与 build 辅助)。npmGitHub
  • Webpack Module Federation(作为依赖共享的对比方案)