05 · 资源转译、Blob 执行与依赖共享
脚本转译主路径:transpileScript
为何要先 removeAttribute('src') 再 fetch
若保留 src,浏览器会立即按原生逻辑加载执行,沙箱来不及注入 makeEvaluateFactory 包装代码:
// 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:
// 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;设计原因:
- 执行环境:代码必须在
with(伪全局)中跑,Blob 包装是桥梁。 - 调试与行为一致:执行时
document.currentScript.src等若长期指向blob:,会与生产环境不一致;短暂切回真实 URL 缓解该问题。 - 内存:
revokeObjectURL避免 Blob 泄漏。
同步脚本与 defer 链
同步外链脚本在 fetch 转译完成后才把 src 填回,因此需 prevScriptTranspiledDeferred 等待前序 defer/同步链完成,以贴近浏览器「先前排队的脚本先执行」的语义(同文件 114-118 行附近)。
Link:preload 与 sandbox 的 as 修正
当脚本改为「手动 fetch + Blob 执行」时,浏览器对 rel=preload as=script 的缓存未必与 fetch() 共享。postProcessPreloadLink 在 REMOTE_ASSETS_IN_SANDBOX 分支把 as 改为 fetch,注释引用 StackOverflow / MDN 关于 CORS preload 的讨论:
/**
* 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
*/开发环境若缺少 crossorigin 会 warn,与 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.ts 中 findDependency 对 peerDeps 已匹配集合的检查。
可简述为:这是运行时依赖对齐,不是 webpack Module Federation 的容器协议;需要构建侧产出依赖图元数据。
样式与 styled-components 等
dynamicAppend/common.ts 等处有 FIXME:为触发样式计算或脚本执行,有时需把节点挂到 真实 document(detached 容器不渲染)。说明样式隔离/Shadow DOM 未默认启用时,子应用样式仍是全局 CSSOM,需要工程规范(BEM、CSS Modules、原子化)配合。