08 · 关键设计决策、依据与技术债
下列条目均可在仓库内通过注释或 FIXME/TODO 溯源。
1. 基于 single-spa 做应用调度,而不是自研路由内核
现象:registerApplication、mountRootParcel、ParcelConfigObject 贯穿 registerMicroApps / loadMicroApp。
原因:微前端「何时挂载/卸载」与路由、URL、应用状态机强相关;single-spa 提供了成熟语义与生态,Qiankun 专注 加载与隔离。
2. HTML 入口 + 流式 writable-dom
原因:降低 TTFB 到首屏可解析内容的间隔;阻塞资源窗口内做 preload。
代价:字符串级 <head> 替换对 chunk 切分敏感(TagTransformStream FIXME)。
3. Head 虚拟化为自定义标签
原因:隔离子应用 head 元数据,配合 document patch 把 document.head 映射到容器子树。
依据:qiankunHeadTagName 常量(packages/sandbox/src/consts.ts)与 loader 替换表一致。
4. 沙箱用 Proxy 膜 + with(this),而非 iframe
收益:DOM 集成简单、实例成本相对低、与主应用共享同一渲染管道。
代价:无法像 iframe 那样进程级隔离;存在 白名单写回真实 window(SystemJS)、间接 eval 等逃逸面;依赖团队对子应用代码约束。
5. 入口脚本必须唯一且带 entry
原因:明确 loadEntry Promise 的 settle 条件,并与 latestSetProp 配对。
依据:Loader 抛错与 AGENTS.md ANTI-PATTERN。
6. Blob 执行后还原 script.src
原因:在保持沙箱执行的前提下,让 document.currentScript、错误栈中的 URL 等更接近原生。
依据:script.ts 内注释与 revokeObjectURL 配套。
7. preload as=fetch 修正
原因:手动 fetch 执行脚本时,与 as=script 的 preload 缓存不对齐。
依据:link.ts 注释中的 StackOverflow / MDN 链接。
8. 冻结属性直出(issue #2015)
Membrane get trap 对 frozen 属性直接返回值、跳过重绑定,注释指向 https://github.com/umijs/qiankun/issues/2015,避免破坏不可变全局对象的语义。
技术债与 FIXME/TODO 汇总(源码检索)
| 位置 | 摘要 |
|---|---|
loadApp.ts | beforeLoad 与 loadEntry 异步插入/同步脚本执行顺序难以严格协调 |
loader/index.ts | 非沙箱模式 resolve 占位、body 替换未做 |
TagTransformStream.ts | 非标准 HTML chunk、缺 <head> 时需补全策略 |
membrane/index.ts | SystemJS 间接 eval → 白名单逃逸 |
forStandardSandbox.ts | 不应依赖全局 __currentLockingSandbox__;getCurrentRunningApp 不可靠(PR #2415) |
dynamicAppend/common.ts | detached 容器需挂回真实 document 才能触发样式/脚本行为 |
sandbox/index.ts / types.ts | snapshot 沙箱、GC 未实现 |
historyListener.ts | umi unmount 相关 FIXME |
assets-transpilers/index.ts | script 文本场景待处理 |
简明表述建议:Qiankun 3 明确把部分问题标为 FIXME,说明维护者承认 时序、逃逸面、保活/快照 等仍是开放题;社区关于「子应用保活」的讨论可参考 Issue #3057 等,与框架无关的保活方案(不卸载 DOM、由框架层缓存 vnode 等)需自行权衡。
下一节
见 09-常见问题.md。