# 自包含模拟包:把 `file://` 下的不可能变成可能 ## 背景 `pnpm dev` 起本地服务、在浏览器里调壁纸,这条路径很好用,但它有一个硬前提:**有一个 HTTP 服务在跑**。要给别人看一眼效果、要在没装依赖的机器上确认"这档壁纸到底长什么样"、要把某一版效果截图存档,都得先把服务起起来。于是有了"自包含包"这个需求:一个 `sim/index.html`,双击就能开,不依赖 `pnpm dev`、不依赖任何服务器、也不依赖网络。 一开始以为这只是"把资源内联进 HTML"。实际做下来,`file://` 这个环境把四件平时根本不会注意到的事变成了拦路虎,每一件都值得记下来。 ## 决策 ### 1. 产物位置:`<分发根>/sim/index.html`,不往 `sim/` 里复制任何资源 自包含包是**分发目录的一部分**,不是一份独立产物: ``` dist/releases/single-hsr-kv37/ index.html ← 发布用(ES module,需要 http) project.json preset.js images/ effects/ audios/ ← 布局见 ADR 0008:现在是 spines/<骨架名>/ scene/ audios/ sim/index.html ← 自包含(经典脚本 + 全内联,双击可开) ``` 好处是它天然跟着分发走:拷走整个分发目录,发布用的那份和调试用的那份一起走。代价是页面不在分发根下,所以**资源根**和**模块根**要分开算——这是本 ADR 第 4 条,也是这次最费时间的一个坑。 资源一律不复制进 `sim/`。复制会让"分发目录里多出一份重复的 2 MB 贴图",且一旦漏拷一个就是白屏;全部内联则"要么整页可用,要么构建期就报错",没有中间状态。 ### 2. 一切内联:模块、资源、音频都变成页面里的字节 `file://` 下浏览器把页面当成 `origin: null`,于是: | 做法 | `file://` 下 | 结论 | | --- | --- | --- | | `