Files
SpineWallpaper/.scratch/build-pipeline/issues/12-dev-hot-reload.md
T
Shuery 3f11426964 feat: TS 构建管线、Spine 抓取器与 wallpapers/ 唯一真相来源
把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。

- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
  (.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
  抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)

同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
  且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
  目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)

pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
2026-10-02 01:27:02 +08:00

3.4 KiB

12 — pnpm dev 热更新

Status: resolved Type: task

需求

pnpm dev 开发模式支持热更新

做法

监听 src/ 与 wallpapers/,按改动只跑必要的那几步构建,然后经 SSE 通知浏览器。

改动 跑什么 浏览器怎么更新 实测
src/styles/*.css 重铺产物 只换样式表 850 ms
src/runtime/* tsc runtime + 重铺产物 整页刷新 1.1 s
src/simulator/* tsc simulator(产物里也带模拟器时再重铺) 整页刷新 600 ms
wallpapers/**、模板、vendor、VERSION 重铺产物 整页刷新 850 ms
构建失败 — 只报错,不刷新 —

全量 pnpm build 是 2.2 秒;按改动拆开之后,改样式只要 0.85 秒,改模拟器 0.6 秒。

为什么 CSS 单独一条路

整页刷新要重新下载 3.4 MB 骨架、重建 Spine 播放器、面板状态全丢。改样式时这些全是白费—— 换一下 <link> 的 href(加个 ?t= 时间戳)就够了。实测确认:换完样式表, window 上的标记还在,也就是页面确实没有重载。

为什么构建失败不刷新

刷新会把一个半成品页面端上来,而错误信息在控制台里——很容易被当成"改了没生效"。 现在构建失败只在左下角显示一条红药丸,页面保持原样。

为什么状态药丸挂在 <html> 下

与模拟器面板同一个理由:body 上会被打 transform/filter(翻转、颜色选项), 挂 body 里会跟着壁纸一起被镜像、被调色。

两个必须记住的约束

  1. 绝不能监听 dist/。构建写 dist/,监听它就是一个死循环。只监听 src/、wallpapers/、VERSION。
  2. --with-sim 产物里也有一份模拟器,而且它的驱动排在调试服注入的那份之前(幂等闸先到先得)。 所以那种构建下,改模拟器必须连产物一起重铺,否则浏览器会一直跑旧模拟器。 调试服启动时会探一次产物里有没有模拟器,据此决定要不要多跑那一步。

调试期代码不许进产物

热更新客户端连的是 /__dev/events——那在真实 WE 里、在任何静态托管上都不存在。 它由 tools/dev.ts 的 html() 注入,tools/build.ts 的 renderIndexHtml 完全不碰它。

这条钉进了 check:dist,不再靠人工抽查。实测过它会拦:

✗ collection-all/index.html 里有调试服专属的 /__dev/events

(一个从不失败的检查等于没有检查——所以注入标记确认它会红,再还原。)

三种产物都确认过干净:默认产物 0 处、--with-sim 产物 0 处、自包含包 0 处。

验证

tools/checks/verify-hot-reload.mts:真的改源文件,用"页面上的标记还在不在"区分两种更新方式 (只看"页面变了没有"是分不出"整页刷新"和"只换样式表"的):

  • 页面注入了热更新客户端;/__dev/events 返回 text/event-stream
  • 改 CSS → 被改的那条样式表 href 换成带 ?t= 的新地址、标记还在(没整页重载)、换上的确实是新内容
  • 改 runtime → 标记被抹掉(整页刷新)、刷新后面板重新挂上
  • 测完还原源文件,并确认仓库里没有探针注释残留

其余回归全绿:pnpm check、调试服与浏览器冒烟、面板还原度、四档自包含包 file://、 静态托管预览、6 组渲染与重构前逐像素相同。