介绍
什么是微前端
- 微前端是一种架构理念,借鉴了微服务的思想,将一个大型的前端应用拆分为多个独立、自治的子应用,每个子应用可以由不同团队、使用不同技术栈独立开发和部署,最终聚合为一个整体产品。
为什么选择无界
- 无界(Wujie)是腾讯团队推出的一款新一代微前端框架。
- 它采用 Web Components + iframe 沙箱 的方式:即通过天然的 iframe 隔离 JS 运行环境,以及 Web Component/Shadow DOM 为样式隔离提供支持。
- 核心特点
- 🚀 开箱即用:主/子应用接入成本低。
- 🧩 高性能:子应用首屏打开快,运行环境隔离更彻底。
- 🔄 路由同步:支持子应用路由状态与主应用同步。
- 💬 通信系统:提供 props、事件总线、共享状态机制。
- ♻️ 技术栈无关:支持 Vue、React 等。
适用场景
- 大型平台型项目(如企业管理后台、电商后台、SaaS 系统)
- 多团队协作开发的大型前端项目
- 系统重构期,需"渐进式"升级的项目
- 多业务线共存,每个团队维护一个功能模块
项目搭建
整体架构
- 使用的是monorepo架构,目录结构如下,具体可参考我的wujie工程源码,其中common 是公共库(供主应用和子应用通用的代码和资源),main 是主应用工程,subs/sub-react 是react框架的子应用工程,subs/sub-vue 是vue框架的子应用工程

主应用
- 各个工程都是直接运行pnpm create vite 命令创建的,主应用使用的是vue3+vite+typescript搭建的
- 页面内容如下图,采用常用的后台管理系统布局方式,分为logo区域、菜单栏、头部、面包屑、子应用展示区、尾部等几个部分,其中子应用展示区用来展示切换左侧菜单栏或者路由变化时的不同子应用

- 主应用接入Wujie,我这里使用的无界二次封装的wujie-vue3,在main目录里使用pnpm i wujie-vue3 -S 安装依赖包,然后在main目录的src/main.ts文件里引入依赖包,vue实例使用use注册Wujie组件,命令如下
js
import WujieVue from "wujie-vue3";
Vue.use(WujieVue); 或 createApp(App).use(Wujie);
- 在想要展示子应用的地方使用如下代码,就能显示出来子应用的内容了,如果是本地服务需要提前启动,如果只是单纯展示子应用是不需要改造子应用的代码的,我在src/components里创建了SubReact.vue 和SubVue.vue两个组件分别挂载react子应用和vue子应用,为了能注册路由和后续的路由同步、路由跳转
js
<WujieVue name="xxx" :url="xxx" />
子应用
react子应用
- 使用的是react+vite+typescript搭建的
- 页面内容如下图,首页及列表页,分为列表页和详情页为了实现react子应用内部的路由跳转,跳转到Vue子应用 按钮是为了直接从react子应用跳转到vue子应用

vue子应用
- 使用的是vue3+vite+typescript搭建的
- 页面内容如下图,首页及列表页,分为列表页和详情页为了实现vue子应用内部的路由跳转,跳转React子应用 按钮是为了直接从vue子应用跳转到react子应用

生命周期
- 无界提供一整套的生命周期钩子供开发者调用,如果子应用没有做生命周期的改造,那么 beforeMount、afterMount、beforeUnmount、afterUnmount 这四个生命周期将不会调用,具体的生命周期钩子函数介绍可以参考生命周期,如下只演示sub-vue子应用的生命周期改造,其他框架改造可以参考生命周期改造
js
// sub-vue的src/main.ts
if (window.__POWERED_BY_WUJIE__) {
let app: ReturnType<typeof createApp>;
window.__WUJIE_MOUNT = () => {
app = createApp(App);
app.use(router).mount('#app');
};
window.__WUJIE_UNMOUNT = () => {
app.unmount();
};
} else {
createApp(App).use(router).mount('#app');
}
- 改造后就可以直接使用这些生命周期钩子了,如下代码
js
// main的src/components/SubVue.vue
<WujieVue
name="sub-vue"
url="http://localhost:5175/"
:beforeLoad="beforeLoad"
:beforeMount="beforeMount"
:afterMount="afterMount"
:beforeUnmount="beforeUnmount"
:afterUnmount="afterUnmount"
/>
const beforeLoad = () => {
console.log('sub-vue beforeLoad');
};
const beforeMount = () => {
console.log('sub-vue beforeMount');
};
const afterMount = () => {
console.log('sub-vue afterMount');
};
const beforeUnmount = () => {
console.log('sub-vue beforeUnmount');
};
const afterUnmount = () => {
console.log('sub-vue afterUnmount');
};
通信系统
window通信
- 子应用想要获取主应用window上的全局变量使用window.parent.xxx获取,如下代码
js
// main的src/main.ts
window.a = '666'; // 设置父应用全局变量
// sub-vue的src/main.ts
console.log('父应用全局变量', window.parent?.a); // 父应用全局变量 666
- 主应用想要获取子应用window上的全局变量使用window.document.querySelector('iframe[name=子应用id]').contentWindow.xxx获取,不过需要等待子应用挂载完成才能获取到,所以需要放到afterMount生命周期内,如下代码
js
// sub-vue的src/main.ts
window.b = '777'; // 设置vue子应用全局变量
// main的src/components/SubVue.vue
const afterMount = () => {
console.log(
'vue子应用全局变量',
(
window.document.querySelector(
"iframe[name='sub-vue']"
) as HTMLIFrameElement
)?.contentWindow?.b
); // vue子应用全局变量 777
};
props通信
- 主应用通过 props 注入数据或方法,子应用使用 $wujie 来获取,如下代码
js
// main的src/components/SubVue.vue
<WujieVue name="sub-vue" url="http://localhost:5175/" :props="{ jump }"/>
// sub-vue的src/views/Layout.vue
const { jump } = window.$wujie?.props || {};
eventBus通信
- 无界提供一套去中心化的通信方案,主应用和子应用、子应用和子应用都可以通过这种方式方便的进行通信,如下代码
js
// main的src/main.ts
const { bus } = Wujie;
bus.$on('jump', (path: string) => {
router.push(path);
});
// sub-vue的src/views/Layout.vue
window.$wujie?.bus.$emit('jump', item.path);
路由跳转
- 子应用内部跳转不受影响,子应用跳转主应用或者另一个子应用,可以使用如上props通信传入的jump方法
js
window.$wujie?.props?.jump('/sub-vue');
- 也可以使用如上eventBus通信注册的jump方法
js
window.$wujie.bus.$emit?.('jump', '/sub-vue');
- 其他方式跳转可以参考路由跳转
路由同步
- 只需要开启sync参数即可开启路由同步功能,当子应用路由变化或主应用路由变化时,在浏览器刷新、前进、后退都能实现路径的同步
js
<WujieVue name="sub-vue" url="http://localhost:5175/" :sync="true" />
应用共享
- npm包共享:如果多个应用共用同一个npm依赖包,基于monorepo架构,可以使用 -W 命令参数,把依赖包安装到工作空间的根目录,这样每个应用就都能直接引入和使用了,如下代码我共用sass包
js
pnpm i sass -W
-
cdn资源共享
- 第一种就是只在主应用中引入,如下代码在main的index.html的head标签内引入lodash,需要在子应用的所有js执行前把主应用window上的变量赋值到子应用的window对象上如window._ = window.parent._,这样子应用不需要引入就能直接使用,但是子应用单独启动是无法使用,可以使用第二种方式
js// main的index.html <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> // main的src/components/SubVue.vue <WujieVue name="sub-vue" url="http://localhost:5175/" :plugins="[ { jsBeforeLoaders: [{ content: 'window._ = window.parent._' }] } ]" />- 第二种是主应用和子应用都引入,如下代码主应用正常引入,子应用引入时需要添加ignore ,防止在微前端环境cdn资源被重复引入,也可以不添加ignore 标志而采用无界的插件将这个脚本排除在外,如第二段代码,无界还有别的插件,具体可以参考插件系统
js// main的index.html <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> // sub-vue的index.html <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js" ignore></script>js// main的index.html <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> // main的src/components/SubVue.vue <WujieVue name="sub-vue" url="http://localhost:5175/" :plugins="[ { jsExcludes: [ 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js' ], jsBeforeLoaders: [{ content: 'window._ = window.parent._' }] } ]" /> // sub-vue的index.html <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
源码分析
目录介绍
- 源码地址wujie,其中packages文件夹里的是源码,其中wujie-core 是无界核心源码包,wujie-react 是针对react框架封装的适配包(即wujie-react),wujie-vue2 是针对vue2框架封装的适配包(即wujie-vue2),wujie-vue3是针对vue3框架封装的适配包(即wujie-vue3),这里主要介绍wujie-core/src里无界的核心代码
理解基础设施
constant.ts:
定义了一系列在运行时全局使用的常量:包括自定义数据属性名(用于在 DOM 上标记子应用或容器状态)、一些固定 class/键名、默认内联样式、loading SVG,以及一系列提示字符串(用于对外/对内错误与使用提醒)。
utils.ts:
汇集了 Wujie 的常用工具函数与类型检查逻辑,主要职责包括:
- 类型判断与缓存(
isFunction、isCallable、isBoundedFunction、isConstructable等),并对函数可调用性/绑定做缓存以提升性能与避免重复绑定。 - 解决子应用间函数污染的问题(
setFnCacheMap+getTargetValue),通过对函数进行按目标对象绑定并缓存来避免多个子应用拿到同一原始函数导致 this/context 错位的问题。 - URL / 路由解析工具(
anchorElementGenerator、appRouteParse、getAnchorElementQueryMap、getSyncUrl等)用于将相对路径/短路径/查询参数解析成需要的值。 - DOM 操作相关工具(
setAttrsToElement、getDegradeIframe、fixElementCtrSrcOrHref)用于给 iframe/元素设置属性,或将相对路径转换为绝对路径以避免子应用内资源加载错误。 - 配置合并函数
mergeOptions用于把用户传入的startOptions/preOptions与之前缓存的配置合并成最终使用的选项。 - 事件触发器、日志包装、微任务调度(
eventTrigger、warn、error、nextTick)。 - 插件钩子执行
execHooks与函数链组合compose等工具。
common.ts:
是 Wujie 沙箱的核心工具与常量模块,主要职责包括:
- 全局缓存管理:维护子应用的沙箱实例(
Wujie)和配置(cacheOptions)的全局缓存 (idToSandboxCacheMap),保证子应用重复加载时复用实例或配置,同时提供增删查功能。 - DOM / Window 代理策略:定义 document、window 属性和方法的代理规则,包括哪些属性从 shadow DOM 获取,哪些从主应用 document 获取,以及需要特殊处理的事件和原生方法快照 (
rawXXX)。 - 事件挂载策略:明确主应用、子应用、iframe window 的事件注册方式,防止事件穿透和冲突,例如
appDocumentAddEventListenerEvents、mainDocumentAddEventListenerEvents、appWindowAddEventListenerEvents。 - 元素路径与属性辅助:定义需要修正相对路径的元素和属性 (
relativeElementTagAttrMap),用于保证子应用资源加载正确。 - 保存原型方法:对
HTMLElement.prototype、Node.prototype、window等核心原生方法做快照保存,例如appendChild、addEventListener等,确保沙箱内部或子应用篡改 DOM / 事件不会破坏原生能力,也方便在沙箱内调用原始方法。
初始化与渲染流程
index.ts:
是 Wujie 的 入口模块,主要负责子应用的初始化、管理、启动和销毁。它核心包含以下几个功能和函数:
- 运行环境初始化
- 在某些情况下(主应用代码在子应用环境中执行),会强制中断主应用运行(
stopMainAppRun()) - 处理子应用内部跳转(
processAppForHrefJump) - 初始化 WebComponent 容器(
defineWujieWebComponent) - 如果浏览器不支持 Wujie 特性,会发出告警(
warn(WUJIE_TIPS_NOT_SUPPORTED))
- 在某些情况下(主应用代码在子应用环境中执行),会强制中断主应用运行(
- 缓存与配置管理:
setupApp(options):将子应用配置缓存到全局 map 中,方便后续快速启动或预加载使用。 - 启动子应用:
startApp(startOptions):启动或激活子应用沙箱实例。- 如果子应用已初始化,会根据保活状态(alive)快速激活,并处理生命周期回调(beforeLoad、beforeMount、afterMount、activated 等)。
- 如果子应用未初始化,则创建新的
WuJie沙箱,异步加载 HTML、CSS 和 JS,通过 loader、插件和钩子处理资源,然后将子应用挂载到指定容器。 - 返回一个销毁函数,用于卸载子应用。
- 预加载子应用:
preloadApp(preOptions):在浏览器空闲时间预加载子应用资源(HTML、CSS、JS),但不执行渲染,支持 exec 标记来决定是否立即执行脚本。 - 销毁子应用:
destroyApp(id):销毁指定子应用沙箱实例,释放 iframe 和相关资源。
entry.ts:
是 无界(Wujie)核心加载模块 ,作用是将一个子应用的 HTML 入口解析成可在沙箱中运行的结构化结果。它是整个框架启动子应用的 第一步,主要负责:
- 加载 HTML 模板(远程或本地)
- 解析出
<script>、<link>、<style>等资源 - 通过插件机制过滤或修改资源
- 提供
getExternalScripts、getExternalStyleSheets接口用于后续加载 - 提供
processCssLoader用于将外部样式内联化 - 内部实现了资源缓存与错误处理逻辑
template.ts:
是微前端的 HTML 扫描器和资源管理器 ,processTpl 是核心函数,它把子应用 HTML 模板解析成 模板 + 样式列表 + 脚本列表 + 入口脚本 的结构化信息,为微前端沙箱动态加载和执行子应用提供基础。processTpl 是 template.ts 的核心,它的作用是解析 HTML 模板并提取资源信息,流程可以拆解为以下步骤:
- 清理模板:移除 HTML 注释,为后续解析做准备。
- 解析样式资源
- 遍历
<link>标签:判断是否为样式表;处理相对路径为绝对路径;判断是否 ignore,如果是则生成 ignore 占位符;否则将外部 CSS 收集到样式列表,并生成替换占位符。 - 遍历
<style>标签:支持内联样式收集;判断 ignore 标记;生成内联样式占位符。
- 遍历
- 解析脚本资源
- 遍历
<script>标签:判断是否 ignore、是否模块、是否跨域;判断是否为入口脚本,并标记唯一入口;对外部 script 处理 async/defer/module 属性;对内联 script 收集代码,并忽略纯注释块;生成对应的占位符。
- 遍历
- 生成最终模板结果
- 输出结构化数据:
template:占位符替换后的 HTML;scripts:脚本列表,包括 src、内联内容、模块信息、async/defer/crossorigin 等;styles:样式列表,包括外部和内联 CSS;entry:子应用入口脚本,优先使用标记的 entry,如果没有则取最后一个脚本。
- 输出结构化数据:
- 支持后处理:如果传入了
postProcessTemplate函数,可以对解析结果进一步加工。
DOM 与样式隔离机制
shadow.ts:
是微前端子应用在 DOM 渲染和样式隔离 的核心模块,负责 shadow DOM/iframe 渲染、样式处理、容器管理和生命周期挂载,确保子应用安全、独立、可扩展。核心功能模块包含:
- Web Component 定义与管理
- 定义
wujie-app自定义元素。 - 通过
connectedCallback挂载 shadowRoot 并关联 sandbox,保证子应用在 shadow DOM 中运行。 - 通过
disconnectedCallback卸载 sandbox,管理生命周期。 - 提供
createWujieWebComponent函数快速生成元素。
- 定义
- 模板渲染
- 核心函数 :
renderTemplateToHtml:将 HTML 模板字符串解析为HTMLHtmlElement;替换 head/body,保持多次渲染一致性;遍历元素,自动 patch effect 并修正相对路径属性为绝对路径。 - 渲染到 shadowRoot :
renderTemplateToShadowRoot:插入 HTML、处理 CSS loader、添加遮罩层、保存 shadowRoot 的 head/body;修复parentNode指向 iframe document。 - 渲染到 iframe :
renderTemplateToIframe:替换 iframe 的 documentElement,处理 CSS loader,patch 渲染效果。
- 核心函数 :
- CSS 处理
- 支持 前置/后置 CSS loader(插件机制),处理子应用样式。
- 修复
:root样式为:host,保证 shadow DOM 隔离。 - 支持
@font-face样式单独注入。 - 提供
getPatchStyleElements获取处理后的 style 元素。
- 容器管理
renderElementToContainer:将元素插入指定容器,并防止重复插入。initRenderIframeAndContainer:创建 iframe 并初始化 HTML 内容。clearChild:清空容器或 shadowRoot 内的子节点。addLoading/removeLoading:动态管理 loading 状态,同时保存/恢复容器原始样式。
- iframe 创建与管理
createIframeContainer:生成 iframe,设置 id、样式和降级属性。- iframe 内 document 初始化为标准
<html><head></head><body></body></html>,保证 sandbox 可安全渲染。
effect.ts:
主要负责 微前端子应用的 DOM 和资源沙箱化,确保子应用在主应用中安全、可控地运行,同时支持样式和脚本的正确加载与执行。核心点包含:
- DOM 方法劫持 :
- 重写
appendChild、insertBefore、removeChild、contains等方法,对<script>、<style>、<link>、iframe等关键标签进行特殊处理。 - 对非劫持标签直接调用原生方法,对需要隔离的标签注入到子应用沙箱环境中。
- 重写
- 样式处理 :
- 拦截
<style>标签的innerHTML、innerText、textContent等属性,使用cssLoader对 CSS 进行处理。 - 对动态插入的样式元素进行节流 patch,确保 shadow DOM 或 host DOM 中样式同步生效。
- 支持外部
<link>样式资源的抓取、处理和注入。
- 拦截
- 脚本处理 :
- 拦截
<script>标签,包括外部和 inline 脚本,按顺序执行并支持异步加载。 - 在子应用重复渲染时发出警告,防止污染 iframe 或全局环境。
- 支持 script 标签的 load/error 事件手动触发,保证生命周期完整。
- 拦截
- 事件拦截与管理 :
- patch
head、body的addEventListener/removeEventListener,记录事件,方便在子应用卸载时清空。
- patch
- 外部资源与插件支持 :
- 支持插件 hook 扩展,例如 CSS/JS 的处理、资源排除规则。
- 支持 degrade 模式(降级),不记录事件,适配简单场景或嵌套 iframe。
- 沙箱隔离 :
- 通过
Wujie实例维护子应用状态,如样式元素、脚本队列、iframe、插件等。 - 保证子应用 DOM 和 JS 与主应用隔离,避免相互污染。
- 通过
iframe 机制与沙箱核心
iframe.ts:
主要负责 创建和管理子应用的 iframe 沙箱环境,实现子应用的 DOM、事件、脚本、URL 等隔离与修复,同时保证主应用与子应用路由、资源加载的同步和安全。核心点包含:
- iframe 沙箱创建与初始化 :
iframeGenerator:创建子应用 iframe,注入必要变量和 Wujie 沙箱实例,初始化 DOM、事件和路由。initIframeDom:重建 iframe 的 document 结构,初始化<base>标签修复相对路径,patch window、document、Node 等方法,保证沙箱化。initBase:为 iframe 创建<base>标签,修复相对路径,使子应用资源正确加载。
- iframe 加载与停止 :
stopIframeLoading:阻止 iframe 加载主应用 JS,避免副作用;处理 ObjectURL 特殊情况,保证 iframe 安全执行子应用逻辑。getSandboxEmptyPageURL/disableSandboxEmptyPageURL:生成或禁用空白沙箱页面,用于 iframe 初始化 fallback。
- DOM 与元素 patch :
patchElementEffect:为子应用 DOM 元素打补丁,修复baseURI、ownerDocument,保证元素在沙箱环境下行为一致。- 在
initIframeDom内部,结合patchDocumentEffect和patchNodeEffect,劫持 document、Node 方法(如appendChild、insertBefore、removeChild),确保子应用 DOM 操作在沙箱中安全运行。
- 事件与属性劫持 :
patchDocumentEffect:劫持 document 的addEventListener/removeEventListener,支持 onEvent 属性、document 特殊事件和 proxy 属性,防止子应用事件影响主应用。- 结合
patchNodeEffect,保证插入、移除节点和 shadow DOM 的操作被正确 patch。
- 资源路径修复 :
patchRelativeUrlEffect:拦截子应用关键元素(img、link、script、source 等)的 src/href,保证相对路径资源正确加载。
- 脚本处理 :
insertScriptToIframe:向子应用 iframe 插入 JS 脚本,支持 inline 和外联脚本,内联脚本通过 proxy 执行保证沙箱安全,处理 async 队列、webpack publicPath auto、crossorigin 等问题。
- 路由同步 :
syncIframeUrlToWindow:监听子应用 iframe 的hashchange和popstate,将路由同步回主应用,保持 URL 一致性。
- 降级模式与插件支持 :
- 通过
sandbox.degrade,支持降级模式,不记录事件或直接操作 document。 - 所有 patch 和脚本注入均支持插件 hook(
execHooks),允许用户自定义扩展。
- 通过
- 沙箱隔离与安全性 :
- 子应用 iframe 与主应用同源,但禁止加载主应用 HTML,防止进入主应用路由逻辑。
- 保证子应用 DOM、事件、脚本、URL 等操作只作用在沙箱环境中,避免污染主应用或其他子应用。
sandbox.ts:
主要负责 每个子应用独立沙箱实例的创建、运行管理与生命周期调度,是无界核心中的"运行控制中心"。它将 iframe、Shadow DOM、事件、脚本执行、URL 同步与插件系统整合起来,使子应用在主应用中以独立隔离的方式运行。核心功能包括:
- 沙箱实例构建与缓存管理
- 使用
idToSandboxCacheMap管理所有正在运行的子应用实例,避免重复创建或内存泄漏。 addSandboxCacheWithWujie与deleteWujieById负责子应用挂载与卸载时的缓存管理。
- 使用
- 创建并初始化沙箱环境
- 基于
iframeGenerator创建 iframe,构建独立的浏览器运行环境。 - 执行
proxyGenerator创建 Proxy 沙箱(作用类似微前端 JS 沙箱),隔离全局变量。 - 使用
localGenerator处理本地变量与特定 API 劫持。 - 支持 degrade 模式(直接运行,无 iframe 隔离)。
- 基于
- 渲染体系与 DOM 隔离
- 通过
initRenderIframeAndContainer、renderTemplateToIframe、renderTemplateToShadowRoot创建渲染容器。 - Shadow DOM + iframe 双层隔离确保 DOM、样式、事件互不污染。
- 使用
clearChild与removeLoading管理容器 DOM 结构与加载占位 UI。
- 通过
- 资源与脚本执行流程控制
- 与
entry.ts协作,接收经过解析的ScriptResultList,保证脚本按顺序、按生命周期执行。 - 脚本注入通过
insertScriptToIframe,确保内部在沙箱上下文运行。 - 支持插件式脚本 loader(
getPresetLoaders/plugin),允许用户扩展 JS/CSS 加载行为。
- 与
- 样式隔离控制
- 与
effect.ts及getPatchStyleElements协作,维护子应用样式标签引用,确保卸载时能清理。 WUJIE_DATA_ATTACH_CSS_FLAG标记收集宿主样式,避免样式污染。
- 与
- URL 与路由同步能力
- 与
sync.ts的syncUrlToWindow、syncUrlToIframe、clearInactiveAppUrl组合使用。 - 当子应用内部跳转路由时,可与主应用 URL 同步,避免刷新导致丢失状态。
- 与
- 生命周期体系与事件触发
- 定义完整生命周期
beforeLoad、beforeMount、afterMount、beforeUnmount、afterUnmount、activated、deactivated、loadError。 - 利用
eventTrigger+EventBus派发自定义生命周期和 app 级事件。 - 保证子应用激活、停用、重用时状态一致、可控。
- 定义完整生命周期
- 事件托管与清理
- 对接
effect.ts的事件记录功能,sandbox 在卸载时调用removeEventListener、recoverEventListeners、recoverDocumentListeners。 - 确保子应用卸载时不残留任何事件绑定,避免内存泄漏或事件串扰。
- 对接
- requestIdleCallback 托管
- 对原生
requestIdleCallback包装,若沙箱已销毁则中断任务,避免任务错误执行。
- 对原生
proxy.ts:
主要负责 子应用的全局对象代理 ,尤其是对 window、document 和 location 的沙箱化处理,确保子应用运行在隔离环境中,同时实现 URL 路径和 DOM 操作的安全同步。核心点包含:
- window 代理 (
proxyWindow) :- 使用
Proxy对iframe.contentWindow进行封装,拦截对全局对象的访问和修改。 - 特殊处理
location、self、window属性,返回子应用代理对象,防止污染主应用。 - 对不可配置和不可写的属性直接返回原值,其他属性通过
getTargetValue修正this指向,保证方法在子应用上下文正确执行。 - 拦截设置属性操作,通过
checkProxyFunction校验函数合法性后再赋值,确保安全。
- 使用
- document 代理 (
proxyDocument) :- 对
document的核心方法如createElement、createTextNode进行代理,调用原生方法并通过patchElementEffect修补元素效果,使 DOM 可被沙箱管理。 - 劫持查询方法,如
getElementById、getElementsByTagName、querySelector等,优先从 shadow DOM 查询,必要时回退到原生 document 查询,保证子应用对 DOM 的访问隔离且可控。 - 支持对
documentURI、URL等属性返回子应用沙箱中的路径。 - 对表单、图片、链接等集合提供代理访问,保证 shadow DOM 内元素完整可操作。
- 根据
documentProxyProperties配置列表处理 shadowRoot、宿主属性及方法,实现主应用与子应用 DOM 隔离。
- 对
- location 代理 (
proxyLocation) :- 拦截
href、reload、replace等关键属性和方法,实现 URL 劫持和跳转控制。 href修改会触发locationHrefSet,根据子应用路径重新渲染 iframe,支持降级模式或 shadow DOM 渲染。- 禁用
reload方法,并提供警告提示,防止子应用刷新影响主应用。 - 常量属性(host、hostname、protocol、port、origin)返回对应子应用 URL 元素的值,保证跨域访问安全。
- 其他原生方法和属性通过
getTargetValue保持原生行为。
- 拦截
- 子应用沙箱隔离和 URL 同步 :
- 通过
proxyWindow、proxyDocument、proxyLocation三层代理,使子应用对全局对象的访问和修改全部可控。 - 支持主应用与子应用 URL 的双向同步,通过
pushUrlToWindow更新主应用地址,保证路由一致性。 - 与
effect.ts的 DOM 沙箱协同工作,实现完整的 JS 与 DOM 沙箱化环境。
- 通过
- 降级模式适配 :
- 当子应用为 degrade 模式时,
locationHrefSet会通过替换 iframe 渲染内容而非 shadow DOM,保证低环境下应用仍能运行。
- 当子应用为 degrade 模式时,
插件系统与事件机制
plugin.ts:
主要负责 微前端子应用的 CSS/JS 加载与插件处理,核心功能是将子应用的样式和脚本经过插件加工,保证在主应用中能正确执行,同时支持路径修正和插件扩展。核心点如下:
- CSS/JS Loader
getCssLoader和getJsLoader:生成柯里化的 loader 函数,将 CSS/JS 代码依次经过插件处理,并支持可选的replace预处理函数。- 支持插件链式组合(compose),可在加载前后对代码进行加工,例如路径修正、语法转换等。
- 预置插件和默认插件
defaultPlugin提供了默认cssLoader(处理相对路径)和cssBeforeLoaders(解决浏览器 view-transition 问题)。getPlugins会将用户传入插件与默认插件合并,确保基础功能可用,同时允许用户自定义处理逻辑。
- 影响插件(Effect Loaders)
getEffectLoaders获取所有插件中指定类型的排除或忽略规则(如jsExcludes、cssIgnores)。isMatchUrl判断子应用资源 URL 是否符合这些规则,用于跳过或忽略不需要处理的文件。
- 路径处理
cssRelativePathResolve将 CSS 文件内的相对路径转换为绝对路径,保证子应用在不同 base 路径下资源正确加载。- 支持处理嵌套括号的 URL、忽略 base64 data URI,不破坏 SVG 或内联样式的路径。
- 插件加载顺序
getPresetLoaders用于获取插件中预置 loader(如cssBeforeLoaders、jsAfterLoaders)并按需要顺序组合,保证加载顺序和执行顺序正确。
event.ts:
主要负责 无界微前端的事件总线管理,实现子应用之间以及子应用内部的事件通信机制,同时支持全局事件和一次性事件。核心点包含:
- 全局事件存储 :
- 使用
appEventObjMap存储每个子应用的事件对象。 - 挂载在全局
window.__WUJIE_INJECT上,防止重复创建。
- 使用
- 事件总线类
EventBus:- 每个子应用实例化一个
EventBus对象,通过id与全局事件 map 关联。 - 提供
$on、$once、$off等方法管理事件监听。 - 支持
$onAll和$offAll注册和取消全局事件回调。
- 每个子应用实例化一个
- 事件触发与回调执行 :
$emit触发事件,同时执行当前子应用事件回调和全局事件回调。- 回调执行支持参数透传,保证调用链完整。
- 错误捕获和提示,通过
warn和error函数处理。
- 一次性事件 :
$once方法实现事件触发一次后自动注销回调。
- 安全与警告 :
- 当事件未注册、无回调或参数异常时,使用
warn提示。 - 确保事件操作不会影响其他子应用或全局状态。
- 当事件未注册、无回调或参数异常时,使用
- 清理机制 :
$clear方法可清空当前子应用所有注册事件,避免内存泄漏或残留回调。
多应用通信与路由同步
sync.ts:
负责 主应用和子应用之间的路由同步、URL 推送与清理,确保在微前端架构下,不同子应用的路径状态能够在主应用和 iframe 内部保持一致,同时支持浏览器前进/后退(popstate)事件的处理。核心功能点包含:
- 同步子应用路由到主应用 (
syncUrlToWindow):- 根据子应用路径更新主应用 URL 的查询参数(query string)。
- 支持短路径映射(prefix),可将长路径替换为占位符
{shortPath}。 - 支持同步标记
sync,可选择性清理或同步子应用路径。 - 通过
history.replaceState更新浏览器地址栏,不触发刷新。
- 同步主应用路由到子应用 (
syncUrlToIframe):- 在子应用首次渲染或刷新时,将主应用当前路由同步到子应用 iframe。
- 排除 href 完整 URL(http/https)情况,只处理内部应用路由。
- 使用子应用
inject.mainHostPath修正 iframe 内路径。
- 清理非激活子应用 URL 参数 (
clearInactiveAppUrl):- 针对 hash 模式,已销毁但仍存在 URL 查询参数的子应用进行清理。
- 仅清理已经执行过且非激活、非 href 跳转的子应用。
- 向主应用推送指定子应用 URL (
pushUrlToWindow):- 将子应用路径写入主应用 URL 查询参数,并使用
history.pushState推入浏览器历史记录。 - 可触发浏览器历史记录管理,实现前进/后退导航。
- 将子应用路径写入主应用 URL 查询参数,并使用
- 处理 href 跳转的子应用路由 (
processAppForHrefJump):- 监听
window.popstate事件,处理浏览器前进/后退。 - 支持降级模式(degrade)和 shadow DOM 模式:
- 前进 href:渲染对应子应用或降级 iframe。
- 后退 href:恢复 DOM 到上一次状态。
- 修复事件时间戳,确保子应用事件正确触发。
- 监听