02 · Monorepo 包职责与数据流
包拓扑(依赖方向)
来自仓库根目录 AGENTS.md 的归纳:
qiankun(门面)
├── @qiankunjs/loader
│ ├── @qiankunjs/sandbox
│ └── @qiankunjs/shared
├── @qiankunjs/sandbox
│ └── @qiankunjs/shared
└── @qiankunjs/shared设计原因:把「会频繁迭代、边界清晰」的能力拆包,便于单独测试、按需引用(例如仅研究 loader 时不必拉满 qiankun 的 single-spa 胶水层)。
各包职责速查
| 包 | 主要职责 |
|---|---|
@qiankunjs/shared | transpileScript / transpileLink、三层 fetch 装饰(缓存/重试/非 2xx 抛错)、moduleResolver(依赖图 + semver)、Deferred 与 defer 脚本队列工具 |
@qiankunjs/sandbox | StandardSandbox(Membrane + Compartment)、createSandboxContainer(mount/unmount 时 patch document 等)、各类 patcher 的 free() 清理 |
@qiankunjs/loader | loadEntry:fetch 流 → TagTransformStream → WritableDOMStream(fork writable-dom) |
qiankun | registerMicroApps / start / loadMicroApp、loadApp 编排、addons(如 runtimePublicPath)、与 single-spa 对齐的 parcel 配置 |
loadApp 中的横向切面
loadApp 在创建沙箱后,将 同一套 enhancedFetch 与 nodeTransformer 注入 loader:
53:55:packages/qiankun/src/core/loadApp.ts
const enhancedFetch = makeFetchCacheable(makeFetchRetryable(makeFetchThrowable(fetch)));89:95:packages/qiankun/src/core/loadApp.ts
const containerOpts: LoaderOpts = {
fetch: enhancedFetch,
sandbox: sandboxInstance,
nodeTransformer,
...restConfiguration,
};
const lifecyclesPromise = loadEntry<MicroAppLifeCycles>(entry, microAppDOMContainer, containerOpts);原因:子应用所有「按 URL 拉脚本/样式」的路径尽量走同一 fetch 栈,从而共享 LRU 缓存、重试策略与错误语义,避免沙箱内 fetch 与 loader 入口 fetch 行为不一致。
默认 nodeTransformer 与模块解析
默认实现把 moduleResolver 绑到当前子应用容器 + document.head(主应用通常把依赖图 script 放在 head):
41:44:packages/qiankun/src/core/loadApp.ts
const defaultNodeTransformer: AppConfiguration['nodeTransformer'] = (node, opts) => {
const moduleResolver = (url: string) => defaultModuleResolver(url, microAppDOMContainer, document.head);
return transpileAssets(node, entry, { ...opts, moduleResolver });
};依据:依赖共享需要同时读取子应用与主应用的 script[type=dependencymap] JSON,再在 semver 约束下把子应用的 URL 映射到主应用已加载的同名依赖 URL(见 packages/shared/src/module-resolver/index.ts)。