Template System 收官构建:多主题矩阵、Vendor 抽离、Manifest 联合、Cache 治理

这篇是上一篇的延续:在已有的「远程 ESM 组件发布」架构上,把多主题构建CSS 哈希Vendor 独立包清单(manifest)合并Cloudflare Pages 的缓存头串成一条可发布的产线。


目标升级

  • 多主题矩阵:同一套流水线按 THEME 变量批量构建(coolgeneral、…)。

  • CSS 哈希与解耦:CSS 独立产出、带 hash、与字体分离。

  • Vendor 抽离react / react-dom / react/jsx-runtime 单独打包到 /v1/vendor/,版本可见、所有主题共用。

  • Manifest 统一:主题 JS + 主题 CSS + vendor 入口,统一生成清单文件,供运行时定位资源。

  • 缓存与回滚:hash + 版本目录(/v1/…)+ _headers 精准管控缓存策略。

产物布局

image

我到底在解决什么问题

  • 我需要把主题以远程 ESM 组件的方式发布,供宿主页面按需加载。

  • 我希望产物可版本化可长缓存可观测,并且不会被开发期依赖拖胖

  • 我还要在多主题场景下维持同一套流水线,避免重复劳动和缓存冲突。

一句话:让“主题作为远程模块”像 NPM 包一样可发布、可回滚、可审计,而且首屏更轻。


为什么把 Vendor(React 等)单独打一个包

目的:用 import map 固定运行时依赖,一处托管,多处共享。

  • React、React‑DOM、react/jsx-runtime 这些与主题无关,却常被重复打进每个主题里,既浪费带宽又浪费缓存。

  • 把它们抽成独立的 vendor 产物,发布到统一目录,由 import map 绑定,所有主题公用同一份运行时。

  • 这带来三个直接好处:

    1. 缓存命中率高:一次下载,多主题复用;

    2. 可控升级:想升级 React 只需替换 vendor 映射,不动各主题;

    3. 问题定位清晰:主题问题与运行时问题分层排查。

取舍:vendor 多了一步构建,但换来稳定的运行时基础与长期的缓存收益,在远程组件体系里是“强建议”。


为什么所有文件名都带“版本 + hash”,还要有 manifest

目的:让“缓存友好”和“立即更新”同时成立。

  • hash 让文件具备不可变性(内容一变,URL 必变),可以放心给 immutable 的长缓存头。

  • 版本目录(例如 /v1/…)是对一批产物的快照标记,方便灰度与回滚。

  • manifest 把“逻辑名 → 真实文件名(含 hash)”统一映射:

    • 运行时只认识“逻辑名”(比如 cool-shop-main),

    • 实际 URL 由 manifest 决定(比如 v1/cool/cool-shop-main.abc123.js)。
      这样宿主逻辑稳定资源地址可变更,避免被浏览器或 CDN 的旧缓存“卡死”。

缓存策略上,我将:

  • **静态资源(JS/CSS)**设为长缓存 + immutable

  • 清单(manifest)设为协商缓存(no-cache),保证每次都能感知新版本,同时走 304 降低带宽。


为什么 CSS 不交给 Vite,而是单独用 PostCSS

目的:让 Tailwind 的“扫描范围”完全受主题控制,避免全站打包与误扫。

  • Tailwind 的体积取决于 content 扫描范围。多主题场景里,每个主题都应该只扫描自己的源文件,才能得到最小化、无泄漏的 CSS。

  • PostCSS 独立产线,我可以非常直观地:

    • 通过环境变量切换到 tailwind.<theme>.config

    • 针对每个主题产出“只包含自己类名”的 CSS;

    • 与字体加载完全解耦(字体走 <link>,不掺在 CSS 里)。

  • 这条 CSS 产线独立于 JS,不会被 JS 的入口组织方式牵着走;每个主题的 CSS 都是可预测的、可复用的静态资源。

取舍:实现上多了一条流水线,但换来极致可控的裁剪更干净的部署心智模型


为什么最后还专门“改名加 hash”,再生成一次 manifest

目的:把“内容指纹”和“加载映射”做成两件独立的、可审计的工序。

  • Tailwind/PostCSS 的任务是生成内容,它不一定对产物命名负责;
    我把“加 hash 改名”放到一个小而透明的步骤里:

    • 算一遍内容指纹;

    • 改名为 主题.指纹.css

    • 把结果写入主题的 manifest(或总清单)。

  • 这样一来,内容 → 指纹 → 映射的链路就很清晰:
    任何异常(内容不变、指纹改变、映射缺失)都能被单点定位。

  • 这一步也为后面做 SRI(子资源完整性)预留了“自然的插钩位”。

取舍:再多一个脚本,但在工程审计与发布安全上非常值


为什么最后还要生成 _headers(以 Cloudflare Pages 为例)

目的:把缓存策略写进工件里,确保部署端“一字不差”。

  • 我不希望“缓存策略”散落在口头约定或平台面板里,所以在 dist/ 根写一个 _headers

    • manifestno-cache(协商缓存);

    • 带 hash 的静态资源max-age=31536000, immutable

    • 可选的报表/HTML:短缓存或禁缓存。

  • 这让缓存行为对任何环境都可移植、可版本化,更适合自动化与回滚。


这套设计的“工程价值”到底在哪里

  • 发布可控:版本目录 + hash + manifest → 确定性上线与回滚;

  • 性能友好:vendor 公用、资源长缓存、字体解耦,首屏负担更低;

  • 多主题可扩展:通过环境变量与独立产线,增删主题不影响既有产物;

  • 可观测:体积、依赖、清单、缓存头都变成看得见、比得出的工件;

  • 清晰边界:JS 产物、CSS 产物、运行时 vendor、缓存策略各司其职,故障域清楚。


一句话总结

    • 运行时抽出来(vendor + import map),让主题变轻

    • 命名与映射标准化(版本 + hash + manifest),让发布可控

    • CSS 的裁剪权交还给主题(PostCSS 独立、按主题配置),让体积精准

    • 缓存策略变成工件(_headers),让部署不靠运气

 

posted @ 2025-08-13 05:29  PEAR2020  阅读(9)  评论(0)    收藏  举报