Qiankun vs Wujie:微前端框架深度对比

Qiankun vs Wujie:微前端框架深度对比

基于 Qiankun 3.0 和 Wujie 1.0.22 源码深度分析,从架构设计、隔离方案、资源加载、通信机制、路由处理、预加载策略、插件系统等维度进行全面对比,助你做出最佳技术选型。

一、设计哲学

1.1 架构理念

维度 Qiankun Wujie
基础架构 基于 single-spa 封装 原创双容器架构
隔离思路 Proxy 代理模拟隔离 浏览器原生隔离
核心技术 Proxy + with + Membrane iframe + Shadow DOM + Proxy
设计目标 开箱即用、生态完善 极致隔离、低侵入
包结构 monorepo(sandbox/loader/shared) 单包

1.2 架构图对比

javascript 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        Qiankun 架构                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    主应用 Window                         │   │
│   │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │   │
│   │  │  子应用 A    │  │  子应用 B    │  │  子应用 C    │      │   │
│   │  │             │  │             │  │             │      │   │
│   │  │ ┌─────────┐ │  │ ┌─────────┐ │  │ ┌─────────┐ │      │   │
│   │  │ │ Proxy   │ │  │ │ Proxy   │ │  │ │ Proxy   │ │      │   │
│   │  │ │ Sandbox │ │  │ │ Sandbox │ │  │ │ Sandbox │ │      │   │
│   │  │ └─────────┘ │  │ └─────────┘ │  │ └─────────┘ │      │   │
│   │  └─────────────┘  └─────────────┘  └─────────────┘      │   │
│   └─────────────────────────────────────────────────────────┘   │
│                              ↓                                   │
│                        single-spa                                │
│                  (路由劫持 + 生命周期管理)                         │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                        Wujie 架构                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  wujie-app (Web Component)                               │   │
│   │  ┌───────────────────────────────────────────────────┐  │   │
│   │  │           Shadow DOM ← CSS 完全隔离                │  │   │
│   │  │           子应用 HTML/CSS 渲染                      │  │   │
│   │  └───────────────────────────────────────────────────┘  │   │
│   └─────────────────────────────────────────────────────────┘   │
│                              ↑ Proxy 代理                        │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │  隐藏 iframe ← JS 原生隔离                               │   │
│   │  ┌───────────────────────────────────────────────────┐  │   │
│   │  │  子应用 JS 运行                                     │  │   │
│   │  │  独立 window / document / location                 │  │   │
│   │  │  DOM 操作通过 Proxy 代理到 Shadow DOM               │  │   │
│   │  └───────────────────────────────────────────────────┘  │   │
│   └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

二、JS 沙箱机制

2.1 Qiankun:Proxy + Membrane + Compartment

Qiankun 3.0 采用三层架构实现 JS 隔离:

typescript 复制代码
// 1. Membrane 层:Proxy 代理拦截属性访问
const handler: ProxyHandler<Window> = {
  get(target, p) {
    if (p in endowments) return endowments[p].value;
    if (modifications.has(p)) return modifications.get(p);
    return Reflect.get(target, p);
  },
  set(target, p, value) {
    if (locked) return false;  // 沙箱锁定时禁止修改
    modifications.set(p, value);
    return true;
  },
};

// 2. Compartment 层:with 语句绑定作用域
;(function(){
  with(this){
    const {console,document,...} = this;  // 优化常用变量
    // 子应用代码
  }
}).bind(proxyWindow)();

// 3. Patchers 层:副作用管理
patchWindowListener(sandbox);   // 事件监听
patchInterval(sandbox);         // 定时器
patchHistoryListener(sandbox);  // History 监听

特点

  • modifications Map 记录所有修改,支持快照恢复
  • 锁定机制:inactive 时禁止修改
  • 副作用补丁:手动管理事件、定时器等

2.2 Wujie:iframe 原生隔离 + Proxy 代理

Wujie 利用 iframe 的原生隔离能力:

typescript 复制代码
// 1. 创建同域 iframe(避免跨域)
const iframe = document.createElement("iframe");
iframe.src = mainHostPath;  // 同域
iframe.style.display = "none";

// 2. 阻止 iframe 加载主应用内容
iframeWindow.stop();

// 3. IIFE 包装脚本,替换全局变量
(function(window, self, global, location) {
  // 子应用代码
  window.foo = 'bar';  // 实际操作 proxy
}).bind(window.__WUJIE.proxy)(
  window.__WUJIE.proxy,
  window.__WUJIE.proxy,
  window.__WUJIE.proxy,
  window.__WUJIE.proxyLocation,
);

// 4. Proxy 代理 document 操作到 Shadow DOM
const proxyDocument = new Proxy({}, {
  get(_, propKey) {
    if (propKey === "getElementById") {
      return (id) => shadowRoot.querySelector(`[id="${id}"]`);
    }
    // ...
  },
});

特点

  • iframe 提供完整独立的 window 对象
  • 原生 JS 隔离,无需模拟
  • 事件、定时器自动隔离,无需手动清理

2.3 隔离能力对比

场景 Qiankun Wujie 说明
全局变量污染 ✅ Proxy 拦截 ✅ 原生隔离 都能处理
原型链污染 ⚠️ 需额外处理 ✅ 原生隔离 Array.prototype.xxx
eval/new Function ⚠️ 可能逃逸 ✅ 原生隔离 动态代码执行
定时器清理 需 patch 自动清理 setInterval/setTimeout
事件监听清理 需 patch 自动清理 addEventListener
localStorage 共享 可配置隔离 通过插件实现
Cookie 共享 共享 同域限制

三、CSS 隔离机制

3.1 Qiankun:Scoped CSS / Shadow DOM

typescript 复制代码
// 方式一:Scoped CSS(默认)
// 原始
.container { color: red; }
// 转换后
div[data-qiankun="app-name"] .container { color: red; }

// 方式二:Shadow DOM(实验性)
start({ sandbox: { experimentalStyleIsolation: true } });

问题

  • Scoped CSS 可能有选择器权重问题
  • Shadow DOM 下弹窗样式需要特殊处理

3.2 Wujie:Shadow DOM + CSS Loader

typescript 复制代码
// 1. 创建 Web Component
class WujieApp extends HTMLElement {
  connectedCallback() {
    this.attachShadow({ mode: "open" });
  }
}
customElements.define("wujie-app", WujieApp);

// 2. 渲染到 Shadow DOM
shadowRoot.innerHTML = template;

// 3. :root 转换为 :host
const cssSelectorMap = { ":root": ":host" };

// 4. @font-face 提取到外部(Shadow DOM 内字体不生效)
if (cssRule.type === CSSRule.FONT_FACE_RULE) {
  fontCssRules.push(cssRuleText);
}
shadowRoot.host.appendChild(fontStyleSheetElement);

// 5. CSS 相对路径处理
code.replace(/url\((['"]?)(.*)(\1)\)/g, (_, pre, url, post) => {
  return `url(${pre}${getAbsolutePath(url, baseUrl)}${post})`;
});

特点

  • 原生 Shadow DOM 隔离
  • 自动处理 :root → :host
  • @font-face 自动提取
  • CSS 相对路径自动转换

3.3 CSS 隔离对比

特性 Qiankun Scoped Qiankun Shadow Wujie Shadow
隔离程度
兼容性 一般 一般
弹窗样式 ✅ 正常 ❌ 需处理 ❌ 需处理
全局样式泄漏 可能 不会 不会
:root 支持 ❌ 需转换 ✅ 自动转换
@font-face ❌ 需处理 ✅ 自动处理

四、资源加载机制

4.1 Qiankun:流式加载 + HTML Entry

typescript 复制代码
// 1. Fetch 增强
const enhancedFetch = makeFetchCacheable(
  makeFetchRetryable(
    makeFetchThrowable(fetch)
  )
);

// 2. 流式加载
res.body
  .pipeThrough(new TextDecoderStream())
  .pipeThrough(createTagTransformStream([
    { tag: '<head>', alt: '<qiankun-head>' },  // 避免污染主应用 head
  ]))
  .pipeTo(new WritableDOMStream(container, nodeTransformer));

// 3. 脚本沙箱包装
const wrappedCode = sandbox.makeEvaluateFactory(code, entry);

// 4. defer 脚本队列保证顺序执行

特点

  • 边下载边解析,首屏更快
  • head 标签转换避免污染
  • Fetch 缓存 + 重试机制
  • defer 脚本顺序保证

4.2 Wujie:importHTML + 脚本分类

typescript 复制代码
// 1. 加载 HTML
const { template, getExternalScripts, getExternalStyleSheets } = 
  await importHTML({ url, html, opts });

// 2. 脚本分类执行
const syncScriptResultList = [];   // 同步脚本
const asyncScriptResultList = [];  // 异步脚本
const deferScriptResultList = [];  // defer 脚本

// 3. 构建执行队列
syncScriptResultList.concat(deferScriptResultList).forEach((script) => {
  this.execQueue.push(() => insertScriptToIframe(script, iframeWindow));
});

// 4. 异步脚本不入队列
asyncScriptResultList.forEach((script) => {
  script.contentPromise.then((content) => {
    insertScriptToIframe({ ...script, content }, iframeWindow);
  });
});

// 5. 触发生命周期事件
this.execQueue.push(() => eventTrigger(iframeWindow.document, "DOMContentLoaded"));
this.execQueue.push(() => eventTrigger(iframeWindow, "load"));

特点

  • 脚本分类处理(sync/async/defer)
  • 执行队列保证顺序
  • 正确触发 DOMContentLoaded/load 事件

五、通信机制

5.1 Qiankun:Props + 全局状态

typescript 复制代码
// 1. Props 传递
registerMicroApps([{
  name: 'app',
  props: { user, token, onLogout, utils: { request, storage } },
}]);

// 子应用接收
export async function mount(props) {
  const { user, token, onLogout, utils } = props;
}

// 2. 全局状态(2.x,3.0 已移除)
const actions = initGlobalState({ user: null });
actions.onGlobalStateChange((state, prev) => {});
actions.setGlobalState({ user: newUser });

// 3. 自定义事件
window.dispatchEvent(new CustomEvent('micro-app-event', { detail }));

5.2 Wujie:EventBus + Props

typescript 复制代码
// 1. 全局事件 Map(支持跨应用通信)
export const appEventObjMap = new Map<String, EventObj>();

// 2. EventBus 实现
class EventBus {
  $emit(event, ...args) {
    // 遍历所有应用的事件对象
    appEventObjMap.forEach((eventObj) => {
      if (eventObj[event]) {
        eventObj[event].forEach((fn) => fn(...args));
      }
    });
  }
}

// 3. 主应用使用
import { bus } from 'wujie';
bus.$emit('theme-change', { theme: 'dark' });
bus.$on('user-logout', () => router.push('/login'));

// 4. 子应用使用
window.$wujie.bus.$on('theme-change', ({ theme }) => {});
window.$wujie.bus.$emit('user-logout');

// 5. Props 传递
startApp({ props: { user, api: { getUserInfo, logout } } });
const { props } = window.$wujie;

5.3 通信对比

特性 Qiankun Wujie
Props 传递
事件总线 需自建 ✅ 内置
全局状态 ✅ initGlobalState(2.x) 需自建
跨应用通信 通过主应用中转 ✅ 直接通信
嵌套应用通信 需处理 ✅ 自动支持

六、路由处理

6.1 Qiankun:single-spa 路由劫持

typescript 复制代码
// 1. History API 劫持
window.history.pushState = function(...args) {
  const result = originalPushState.apply(this, args);
  reroute();  // 触发路由变化检查
  return result;
};

// 2. 监听事件
window.addEventListener('popstate', reroute);
window.addEventListener('hashchange', reroute);

// 3. activeRule 匹配
registerMicroApps([{
  activeRule: '/react',  // 字符串
  activeRule: ['/react', '/react-app'],  // 数组
  activeRule: (location) => location.pathname.startsWith('/react'),  // 函数
}]);

// 4. 子应用需要设置 basename
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/react' : '/'}>

6.2 Wujie:URL Query 同步

typescript 复制代码
// 1. 子应用路由存入主应用 URL
// 主应用: https://main.com/home?vue3=%2Fuser%2F123
// 子应用: /user/123

// 2. History 劫持同步
history.pushState = function(data, title, url) {
  rawHistoryPushState.call(history, data, title, mainUrl);
  syncUrlToWindow(iframeWindow);  // 同步到主应用 URL
};

// 3. 短路径优化
startApp({
  sync: true,
  prefix: { 'u': '/user' },  // /user/123 → {u}/123
});

// 4. 刷新恢复
const syncUrl = getSyncUrl(id, prefix);  // 从 URL 获取子应用路由

6.3 路由对比

特性 Qiankun Wujie
路由模式 hash/history hash/history
URL 同步 需配置 ✅ sync 模式
路由隔离 共享 history 独立 history
刷新恢复 需处理 ✅ 自动恢复
短路径优化 ✅ prefix 配置
子应用改造 需设置 basename 无需改造

七、预加载策略

7.1 Qiankun:多策略预加载

typescript 复制代码
// 1. 配置方式
start({ prefetch: true });  // 首屏后预加载所有
start({ prefetch: 'all' });  // 立即预加载所有
start({ prefetch: ['react-app'] });  // 指定应用
start({
  prefetch: (apps) => ({
    criticalAppNames: ['react-app'],  // 关键应用立即加载
    minorAppNames: ['vue-app'],       // 次要应用空闲时加载
  }),
});

// 2. 空闲时加载
requestIdleCallback(async () => {
  const { getExternalScripts, getExternalStyleSheets } = await importEntry(entry);
  requestIdleCallback(getExternalStyleSheets);
  requestIdleCallback(getExternalScripts);
});

// 3. 基于网络状况
if (connection.effectiveType === 'slow-2g') {
  return { criticalAppNames: [], minorAppNames: [] };
}

7.2 Wujie:preloadApp

typescript 复制代码
// 1. 预加载
import { preloadApp } from 'wujie';
preloadApp({ name: 'vue3', url: 'http://localhost:7300/' });

// 2. 预执行(创建沙箱但不渲染)
preloadApp({ name: 'vue3', url: '...', exec: true });

// 3. 保活模式(状态保留)
startApp({ alive: true });  // 切换时不销毁,直接激活

7.3 预加载对比

特性 Qiankun Wujie
预加载资源
预执行 ✅ exec 模式
保活模式 ✅ alive 模式
空闲加载 ✅ requestIdleCallback
自定义策略 ✅ 函数配置
网络感知 可实现

八、插件/扩展系统

8.1 Qiankun:生命周期钩子

typescript 复制代码
registerMicroApps(apps, {
  beforeLoad: [(app) => console.log('beforeLoad', app.name)],
  beforeMount: [(app) => console.log('beforeMount', app.name)],
  afterMount: [(app) => console.log('afterMount', app.name)],
  beforeUnmount: [(app) => console.log('beforeUnmount', app.name)],
  afterUnmount: [(app) => console.log('afterUnmount', app.name)],
});

8.2 Wujie:完整插件系统

typescript 复制代码
startApp({
  plugins: [{
    // HTML 处理
    htmlLoader: (code) => code,
    
    // JS 处理
    jsExcludes: [/google-analytics/],  // 排除
    jsIgnores: [/ad\.js/],             // 忽略执行
    jsBeforeLoaders: [{ content: 'window.ENV = "prod"' }],
    jsLoader: (code, url) => code.replace(/api\.dev/g, 'api.prod'),
    jsAfterLoaders: [{ src: 'https://cdn.com/init.js' }],
    
    // CSS 处理
    cssExcludes: [],
    cssBeforeLoaders: [{ content: ':root { --theme: dark; }' }],
    cssLoader: (code, url) => code,
    cssAfterLoaders: [],
    
    // 事件钩子
    windowAddEventListenerHook: (win, type, handler) => {},
    documentAddEventListenerHook: (win, type, handler) => {},
    
    // DOM 钩子
    appendOrInsertElementHook: (element, iframeWindow) => {},
    patchElementHook: (element, iframeWindow) => {},
    
    // 属性覆盖
    windowPropertyOverride: (iframeWindow) => {
      iframeWindow.localStorage = customStorage;
    },
    documentPropertyOverride: (iframeWindow) => {},
  }],
});

8.3 扩展能力对比

特性 Qiankun Wujie
生命周期钩子
JS Loader
CSS Loader
资源排除/忽略
前置/后置脚本
事件拦截
DOM 拦截
属性覆盖

九、生命周期

9.1 Qiankun 生命周期

erlang 复制代码
beforeLoad → bootstrap → beforeMount → mount → afterMount
                              ↓
                    beforeUnmount → unmount → afterUnmount

子应用必须导出:

typescript 复制代码
export async function bootstrap() {}
export async function mount(props) {}
export async function unmount(props) {}

9.2 Wujie 生命周期

erlang 复制代码
beforeLoad → active → start → beforeMount → mount → afterMount
                                    ↓
                          beforeUnmount → unmount → afterUnmount

子应用可选导出:

typescript 复制代码
window.__WUJIE_MOUNT = () => {};
window.__WUJIE_UNMOUNT = () => {};

9.3 生命周期对比

特性 Qiankun Wujie
必须导出 ✅ 必须 ❌ 可选
改造成本
独立运行 需判断 __POWERED_BY_QIANKUN__ 自动兼容
生命周期数量 5 个 6 个

十、性能对比

维度 Qiankun Wujie
首次加载 稍慢(创建 iframe)
切换速度 快(保活模式更快)
内存占用 中(iframe 开销)
沙箱创建 快(Proxy) 稍慢(iframe)
样式隔离开销 低(Scoped)/ 中(Shadow) 中(Shadow)

十一、兼容性

特性 Qiankun Wujie
IE 支持 ❌ 需 Proxy polyfill
最低浏览器 Chrome 49+ Chrome 53+
Web Components 可选 必需(有降级)
Proxy 必需 必需

十二、选型建议

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        选型决策树                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   需要极致 JS 隔离?                                             │
│       ├── 是 ──────────────────────────────▶ Wujie              │
│       └── 否 ──▶ 继续判断                                        │
│                                                                  │
│   子应用改造成本敏感?                                            │
│       ├── 是 ──────────────────────────────▶ Wujie              │
│       └── 否 ──▶ 继续判断                                        │
│                                                                  │
│   需要保活模式(状态保留)?                                       │
│       ├── 是 ──────────────────────────────▶ Wujie              │
│       └── 否 ──▶ 继续判断                                        │
│                                                                  │
│   使用 umi 框架?                                                │
│       ├── 是 ──────────────────────────────▶ Qiankun            │
│       └── 否 ──▶ 继续判断                                        │
│                                                                  │
│   需要成熟生态和社区支持?                                        │
│       ├── 是 ──────────────────────────────▶ Qiankun            │
│       └── 否 ──▶ 继续判断                                        │
│                                                                  │
│   需要丰富的插件能力?                                            │
│       ├── 是 ──────────────────────────────▶ Wujie              │
│       └── 否 ──▶ 都可以                                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

选择 Qiankun 的场景

  • umi 项目,需要深度集成
  • 团队熟悉 single-spa 生态
  • 对隔离要求不极致,接受 Proxy 方案
  • 需要成熟稳定的解决方案
  • 子应用可以配合改造

选择 Wujie 的场景

  • 需要极致的 JS 隔离能力
  • 子应用改造成本要求低
  • 需要保活模式(频繁切换场景)
  • 需要丰富的插件扩展能力
  • 需要路由同步到 URL

十三、总结

维度 Qiankun Wujie 胜出
JS 隔离 Proxy 模拟 iframe 原生 Wujie
CSS 隔离 Scoped/Shadow Shadow 平手
改造成本 Wujie
生态成熟度 Qiankun
插件能力 Wujie
预加载 丰富 基础 Qiankun
保活模式 Wujie
路由同步 需处理 内置 Wujie
内存占用 Qiankun
社区支持 Qiankun

最终结论

  • Qiankun:成熟稳定,生态完善,是基于 single-spa 的事实标准,适合对稳定性要求高、有 umi 技术栈的团队
  • Wujie:隔离彻底,改造成本低,创新的双容器架构,适合对隔离要求高、需要保活模式、子应用改造受限的场景

两个框架各有千秋,选型时需要根据项目实际情况,权衡隔离能力、改造成本、团队熟悉度、生态支持等因素综合考虑。


📦 源码参考:

📚 相关专栏:

相关推荐
freeWayWalker2 小时前
【前端工程化】前端代码规范与静态检查
前端·代码规范
C2X2 小时前
关于Git Graph展示图的理解
前端·git
昊茜Claire2 小时前
鸿蒙开发之:性能优化与调试技巧
前端
雲墨款哥2 小时前
从一行好奇的代码说起:Vue怎么没有React的<StrictMode/>
前端
小肥宅仙女2 小时前
告别繁琐!React 19 新特性对比:代码量减少 50%,异步状态从此自动管理
前端·react.js
ohyeah2 小时前
柯理化(Currying):让函数参数一个一个传递
前端·javascript
9坐会得自创3 小时前
使用marked将markdown渲染成HTML的基本操作
java·前端·html
Hilaku3 小时前
当 Gemini 3 能写出完美 CSS 时,前端工程师剩下的核心竞争力是什么?
前端·javascript·ai编程
最贪吃的虎3 小时前
什么是开源?小白如何快速学会开源协作流程并参与项目
java·前端·后端·开源