从 90KB 到 24KB:我如何把远程 React 组件做成可版本化、可缓存、可观测的主题系统
背景与目标
在 template-system 里,我要把主题以 远程 ESM 组件的形式发布,要求:
- 
运行时只加载组件与纯数据(JSON Schema),不夹带开发期工具库; 
- 
产物可版本化( /v1/...)、可长缓存(hash 文件名)、可观测(体积分布、依赖结构);
- 
CSS 与字体加载解耦,避免阻塞与冗余。 
总体架构
- 
JS 产物:Vite lib 模式,多入口( cool-shop-header.tsx/cool-shop-main.tsx)。- 
react、react-domexternal,避免把宿主依赖吃回包里。
- 
输出路径规范:入口文件走 dist/${theme_version}/cool|general/...,非入口 chunk 统一落在dist/${theme_version}/vendor/。
- 
自定义插件 writeManifest在generateBundle中用emitFile生成${theme_version}/manifest.json,记录 “入口名 → 实际文件名(含 hash)” 的映射,方便远程加载器按名称定位最新文件。
 
- 
- 
Schema 产物:开发期脚本 gen:schema把 Zod 定义转成纯 JSON落盘;组件侧只import *.schema.json,不引入 zod,JS 体积立降。
- 
CSS 产物:Tailwind 针对单主题独立构建为 dist/v1/cool/cool.css。- 
content精准指向主题源码;
- 
@tailwindcss/postcss+autoprefixer+ 生产态cssnano;
- 
Google Fonts 从 CSS 的 @import改为 HTML<link>,并配preconnect。
 
- 
- 
观测: rollup-plugin-visualizer输出dist/${theme_version}/stats.html,随构建检查包体积与共享块。
构建流程(scripts)
要点:先 gen:schema,避免把 zod 打进包;JS/CSS 分离产出,独立缓存演进。
关键配置(Vite)
- 
Babel 接管 JSX(锁死 prod 路径): 
- 
输出规范与 external: 
- 
manifest 插件(emitFile 版): 
- 
(可选)删 JS 注释与 console:打开即可 
PostCSS(Tailwind v4)
HTML 中放字体与主题 CSS:
体积与结果
- 
移除运行时 zod 后,入口 JS 从 ~87 KB 降至 ~24 KB(raw);visualizer 不再出现 zod*。
- 
CSS 原始 ~94 KB;CDN 传输(Brotli)通常十几 KB。进一步瘦身靠精准 content、精简动画与按需模块化。
- 
目录按 ${theme_version}归档,CDN 可配max-age=31536000, immutable,新版本走新目录,老缓存自然不破。
常见坑
- 
Tailwind v4 的 PostCSS 插件是 @tailwindcss/postcss,不是tailwindcss。
- 
--config是 PostCSS 的配置文件选项;Tailwind 的配置文件通过插件参数tailwindcss({ config: ... })指定。
- 
生成清单用 emitFile,避免emptyOutDir/目录不存在导致的 ENOENT。
- 
在你的组合下,显式加 Babel JSX 插件能稳定产出 prod 变体,避免 jsxDEV潜入。
下一步
- 
如果需要彻底掌控 runtime 位置,把 react/jsx-runtime也 external,并做一份 vendor(react.js/react-dom.js/react-jsx-runtime.js)上传到${theme_version}/vendor/,由 import map 指向。
- 
做一份首页 critical CSS 内联,其余主题 CSS 延迟加载;同时用 prefetch提前拉远程组件。
- 
远程加载器里加 stale-while-revalidate和重试/熔断策略,提升“冷启动”与弱网容错。

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号