资讯动态

腾讯无界微前端源码解析:iframe沙箱与子应用保活如何骗过浏览器同源策略

发布时间:2026/10/17 2:42:59 来源:尧图企业网站定制
前言深入腾讯无界微前端框架源码解析它如何用隐藏iframe沙箱承载子应用JS、将DOM渲染进主应用Shadow DOM实现弹窗全屏遮罩与子应用保活不丢状态再拆解srcdoc同源继承与proxyLocation伪造location的双层欺骗机制让传统iframe的四大痛点在微前端场景下被逐一化解。我把腾讯无界的源码翻了个底朝天发现它把浏览器给骗了前两天跟一个做前端的老朋友吃饭他跟我吐槽说他们公司的中台系统里塞了七八个子应用全是用 iframe 糊的弹窗永远只能弹在自己那一小块框里遮罩盖不住顶部的导航栏用户天天投诉领导天天问他什么时候能重构。我说你听说过无界吗腾讯开源的那个微前端框架就是拿 iframe 做的。他筷子直接放下了。拿 iframe 做微前端我特别理解他的反应。如果你关注前端这个领域的话iframe 在微前端圈子里名声一直不咋地。隔离是真的隔离js、css、dom 全给你焊死在框里但代价也是真的大。路由不同步用户在 iframe 里点了半天浏览器地址栏纹丝不动一刷新全回到解放前。弹窗割裂fixed 定位只能相对 iframe 自己遮罩永远盖不住全屏。通信麻烦主应用和子应用之间只能靠 postMessage 传字符串跟两个隔着玻璃罩子的人打手势似的。还有慢每次打开都是一次浏览器上下文的重建白屏时间长得让人怀疑人生。所以圈内人的共识基本是微前端想做好就得绕开 iframe用 Proxy 沙箱那种软隔离去硬搓。然后腾讯的工程师反手就把 iframe 捡回来了。我第一反应跟那位朋友一样不信。所以我鬼使神差的把 wujie 的源码 clone 了下来v2.1.0packages/wujie-core 这个目录一个文件一个文件的翻。翻了两天我的结论是之前看到的那份介绍材料基本属实而且真实实现比材料里写的还要骚。它干的事情一句话就能讲清楚。把Web子应用的灵魂和肉体拆开灵魂在iframe肉体在主应用子应用的 JS跑在一个 display none 的隐藏 iframe 里纯粹拿它当沙箱用iframe 有浏览器原生的 window、完整的 history 和 location隔离性拉满一个字都不用改。而子应用的 DOM被抽出来塞进主应用的一个 Web Component 里也就是一个叫 wujie-app 的自定义标签内部套着 Shadow DOM。灵魂在框里肉体在框外。这一拆传统 iframe 的几宗罪直接消掉一大半。弹窗为什么只能弹在框里因为 DOM 长在 iframe 里。而现在 DOM 搬到主应用的文档里了子应用写一个 fixed 定位它相对的就是整个浏览器窗口遮罩想盖多大盖多大。子应用里 document.getElementById 找不到元素怎么办无界用 Proxy 把 iframe 的 document 整个代理了querySelector、getElementById、head、body 这些查询类的接口全部偷偷重定向到主应用的 Shadow Root 上。子应用毫无察觉它以为自己摸的是自己的 document其实摸的是主应用家里那棵 DOM 树。说实话我看到这个设计的时候第一反应是这也太损了但太牛逼了。不过真正让我把源码翻出味道来的是另外两件事。一件是保活一件是同源。这两块是那份材料里着墨最多的地方我逐行对了一下属实而且细节比想象中精巧。先说保活这块。用过微前端的朋友都知道子应用切换最恶心的就是状态丢失用户填了一半的表单切个菜单回来全没了。Vue 有 keep-alive 这种保活方案但微前端的场景下很难做因为应用切换的时候整个实例都被销毁了。无界的做法是离开的时候不断电。具体是这样。主应用路由切走wujie-app 这个自定义标签从页面里被拔出来Web Component 原生的 disconnectedCallback 钩子触发源码在 packages/wujie-core/src/shadow.ts 里就几行转手交给 handleWujieAppDisconnect 处理。这个函数里有一个关键判断如果这个子应用没开保活就调 destroy全销毁该清理清理。但如果开了 alive走的是另一个分支只调 unmount。而 unmount 这个函数我盯着看了很久。它里面到处都是「不」。不调用子应用的销毁钩子。子应用那个 React 根组件、Vue 根实例原封不动留在 iframe 的内存里定时器还在跑响应式变量还在网络请求该回来回来。不清空 Shadow DOM。整棵已经渲染好的 DOM 树连着那个 wujie-app 节点被 sandbox 对象攥在手里常驻 JS 堆内存。它只是不在页面上了不是不在了。也不动 iframe。那个隐藏的 iframe 从头到尾挂在主应用 document.body 的最底下安安静静谁也不许碰它。然后用户切回来了。startApp 一进来就检查哦这实例还在还是保活模式直接走 active。active 里最核心的就一步把内存里那个 wujie-app 节点用原生 appendChild 插回页面容器然后 if (this.alive) return流程结束。不请求 HTML不解析样式不执行任何 JS。就是插回去。离开只是拔下来回来只是插回去你想想看用户视角是什么体验。切走再切回页面瞬间出现表单还在滚动位置还在连子应用里那个 setInterval 计时器都没跳针。这不是加载快这是压根没加载就是从内存里把一块 DOM 拿出来拍回去了。聊到这我想起一个哲学梗忒修斯之船。一艘船的木板被一块块换掉它还是原来那艘船吗。无界给出的答案挺暴力只要灵魂不断电肉体拔下来再插回去它就还是原来那个应用但真正的高潮在后面。顺着保活往下翻我撞上了整个框架里我觉得最NB的部分。前面说了子应用的 JS 跑在 iframe 里DOM 挂在主应用里那主应用就得能随时摸到 iframe 的内部代理它的 document注入它的 window。但浏览器的同源策略不是吃素的一旦 iframe 跟主应用跨了域任何这种跨框操作直接给你抛 SecurityError一个字都不让你看。问题来了。子应用明明部署在自己的域名下比如 sub.domain.com主应用在 main.domain.com这个 iframe 凭什么能跟主应用同源我翻到 packages/wujie-core/src/iframe.ts 的 iframeGenerator 函数看到创建 iframe 那段代码的时候整个人是懵的。它压根没给 iframe 设置 src。它设置的是 srcdoc内容就是一个空白的 HTML 壳head 是空的body 也是空的。空白文档白嫖同源这是 HTML 规范里一个特别冷门的特性通过 srcdoc 生成的文档它的 origin 会强制继承自宿主也就是主应用。零网络请求天然同源。早期要是直接把 src 指到主应用的地址浏览器会真的去加载一遍主应用的页面脚本再跑一遍直接递归死循环。srcdoc 一上请求没有同源白嫖。但事情没完。srcdoc 文档的地址是个诡异的 about srcdoc子应用里的相对路由、fetch 全会出幺蛾子。无界接着用 document open 再 document close 这个老掉牙的 API把文档的 URL 规整回主应用的域名下再用 history replaceState 把子应用的路由路径拼在主应用域名后面写进去。注意写的永远是主应用域名加子应用路径因为域名是主应用的浏览器才放行。那子应用代码里读 window.location.host 的时候不就穿帮了吗它明明以为自己跑在 sub.domain.com。这就是我觉得最骚的一层。无界用 Proxy 造了一个假的 location叫 proxyLocation然后通过一个立即执行函数在子应用代码执行之前把全局的 location 偷偷换成了这个假货。子应用读 host、hostname、origin返回的是子应用自己的域名。读 href底层那串主应用域名的真实地址被 replace 一道把主应用域名还原成子应用域名再给你。子应用调 location.replace 想跳转参数先被反向替换成主应用域名才真正执行。一层”骗“浏览器一层”骗“子应用。对浏览器来说这个 iframe 从头到尾都是主应用的一部分同源随便操作。对子应用来说我明明就跑在我自己的域名下我的 origin 没问题我的路由没问题。两边都被蒙在鼓里只有无界在中间两头传话。我当时看到 proxyLocation 那段代码脑子里蹦出来的词是庄周梦蝶。那个 iframe 里的子应用一直以为自己活在 sub.domain.com其实它从头到尾都活在主应用给它造的一场梦里。可你要较真说它活在梦里吧它渲染出来的 DOM、发起的请求、维护的状态又全是真的。还有一堆收尾的细节比如往 iframe 的 head 里插一个 base 标签把静态资源的解析基准指回子应用的服务器不然子应用里一个相对路径的 logo 图片就会跑去主应用的域名下找404 给你看。材料里提到的那些技巧我逐个对过源码都有对应的实现没有一个是吹的。坦率的讲翻完这一圈我对「用 iframe 做微前端」这个想法的印象彻底反转了。iframe 的问题从来不是 iframe 本身烂而是大家只把它当一个容器用。而无界把它拆成了两半拿它最硬的那一面做沙箱拿 Web Components 去补它最软的那一面做渲染。隔离是原生的性能没有 Proxy 沙箱那种 with 包一层的损耗子应用几乎零改造就能接入连 Vite 这种 ESM 的产物都能直接跑。当然也不是没有代价。同源这套魔法的前提是子应用和主应用能被摆到同一个源下如果子应用域名真的完全独立、跨域到没法处理这套把戏就玩不下去得走降级方案。而且保活模式下子应用的 JS 是永远在后台活着的内存和定时器都占着一口气开太多子应用一样会有性能压力。这些材料里没写我觉得得替它补上真诚是唯一的捷径嘛。子应用独立域名解决方案场景需求核心痛点最佳解决手段实施侧自有独立域名子应用入口及 JS/CSS 跨域子应用 Nginx / CDN 配置Access-Control-Allow-Origin子应用部署层独立域名身份鉴权跨站 Cookie 被浏览器丢弃废弃 Cookie 鉴权改用Authorization: Bearer TokenHeader主子应用业务代码持久化存储防冲突读写主应用 Storage 产生键冲突配置无界插件劫持localStorage添加 App 前缀主应用无界配置外部不可控第三方系统无法配 CORS / 拒绝代码改造1. 无界配置degrade: true2. 主应用 Nginx 代理并清洗 Header主应用配置 / 网关层对了还有个细节我特别喜欢。无界的官方文档里写着这个方案的灵感来自 qiankun 的 issue 区里一个开发者提的问题能不能用 iframe 来做 JS 沙箱。一个 issue一个当时看起来有点异想天开的问题最后长成了一个框架。你说巧不巧我朋友那个天天被 iframe 弹窗折磨的中台上周开始试无界了。他跟我说弹窗能盖住全屏的那一刻他愣了几秒。那种感觉大概就是原来框住你的东西换个用法就是保护你的东西。磨平一些信息差共勉。/ 注意本项目仅在 SyncMein 的500人小群分享。/ 谢谢你看完了我的文章我们下次再见吧。/ 作者明察/ 投稿或爆料请联系邮箱mingchagqmg.com标签无界, 微前端, iframe, 沙箱, 保活关键词无界微前端, iframe沙箱, 子应用保活, Shadow DOM渲染, 同源策略欺骗

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑