Skip to content
目录

05 · 资源转译、Blob 执行与依赖共享

脚本转译主路径:transpileScript

为何要先 removeAttribute('src') 再 fetch

若保留 src,浏览器会立即按原生逻辑加载执行,沙箱来不及注入 makeEvaluateFactory 包装代码:

95:97:packages/shared/src/assets-transpilers/script.ts
// We must remove script src to avoid self execution as we need to fetch the script content and transpile it
        script.removeAttribute('src');
        script.dataset.src = src;

Blob URL 执行与 sourceURL 还原

流程:fetch 文本 → 拼接 beforeExecutedListenerScript + sandbox.makeEvaluateFactory(code, src)createObjectURL 赋给 script.src → 监听自定义事件,在执行瞬间 revokeObjectURL 并把 src 改回原始 URL,同时打 dataset.consumed

124:135:packages/shared/src/assets-transpilers/script.ts
// change the script src to the blob url to make it executed in the sandbox
            script.src = URL.createObjectURL(new Blob([codeFactory], { type: 'text/javascript' }));

            window.addEventListener(beforeScriptExecuteEvent, function listener(evt: CustomEventInit) {
              const { s } = evt.detail as { s: HTMLScriptElement };
              if (s === script) {
                URL.revokeObjectURL(s.src);
                // change the script src to the original src while the script is executing
                // thus the script behavior can be more consistent with the native browser logic
                s.src = src;
                s.dataset.consumed = 'true';
                delete s.dataset.src;

设计原因

  1. 执行环境:代码必须在 with(伪全局) 中跑,Blob 包装是桥梁。
  2. 调试与行为一致:执行时 document.currentScript.src 等若长期指向 blob:,会与生产环境不一致;短暂切回真实 URL 缓解该问题。
  3. 内存revokeObjectURL 避免 Blob 泄漏。

同步脚本与 defer 链

同步外链脚本在 fetch 转译完成后才把 src 填回,因此需 prevScriptTranspiledDeferred 等待前序 defer/同步链完成,以贴近浏览器「先前排队的脚本先执行」的语义(同文件 114-118 行附近)。

当脚本改为「手动 fetch + Blob 执行」时,浏览器对 rel=preload as=script 的缓存未必fetch() 共享。postProcessPreloadLinkREMOTE_ASSETS_IN_SANDBOX 分支把 as 改为 fetch,注释引用 StackOverflow / MDN 关于 CORS preload 的讨论:

48:52:packages/shared/src/assets-transpilers/link.ts
/**
         * While the assets are transpiling in sandbox, it means they will be evaluated with manual fetching,
         * thus we need to set the attribute `as` to fetch instead of script or style to avoid preload cache missing.
         * see https://stackoverflow.com/questions/52635660/can-link-rel-preload-be-made-to-work-with-fetch/63814972#63814972
         */

开发环境若缺少 crossoriginwarn,与 MDN 对 CORS-enabled fetches 的说明一致(同文件 55-58 行)。

依赖共享:script[type=dependencymap]

moduleResolver(url, microAppContainer, mainAppContainer) 解析两侧 JSON:

  • 子应用声明某 URL 对应包名、版本、semver range、peerDeps;
  • 主应用声明已加载的同名包实际版本与 URL;
  • satisfies 匹配成功则返回主应用 URL,转译层可走 复用 ObjectURL 路径,避免重复下载与重复执行。

对应实现见 packages/shared/src/module-resolver/index.tsfindDependencypeerDeps 已匹配集合的检查。

可简述为:这是运行时依赖对齐,不是 webpack Module Federation 的容器协议;需要构建侧产出依赖图元数据。

样式与 styled-components 等

dynamicAppend/common.ts 等处有 FIXME:为触发样式计算或脚本执行,有时需把节点挂到 真实 document(detached 容器不渲染)。说明样式隔离/Shadow DOM 未默认启用时,子应用样式仍是全局 CSSOM,需要工程规范(BEM、CSS Modules、原子化)配合。

下一节

06-single-spa与loadApp编排.md

MIT License.