☰
treat 如何工作:构建时静态提取 CSS 的完整编译流程图解
2026/10/14 18:48:02 网站建设 项目流程

【免费下载链接】treat

:candy: Themeable, statically extracted CSS‑in‑JS with near‑zero runtime.

项目地址:https://gitcode.com/gh_mirrors/tre/treat
点击查看免费下载

treat 是一个「构建时静态提取 CSS-in-JS、运行时几乎零开销」的样式方案:样式写在特殊的 treat 文件(.treat.js/.treat.ts)中,在构建阶段于 Node 里编译执行,所有 CSS 规则提前生成并输出为静态样式文件;浏览器里只保留普通的类名字符串。下面结合源码,逐步拆解这套编译流程。

一、核心思想:把「写样式」变成构建期的一次执行

传统 CSS-in-JS 库在浏览器运行时才拼接、注入样式;Sass / Less 用预处理器语言在构建时生成 CSS。treat 的思路介于两者之间:

对比维度Sass / Less传统 CSS-in-JStreat
样式书写语言自定义 DSLJavaScriptJavaScript
CSS 生成时机构建时运行时构建时
浏览器运行时开销无较大近零(不主题化时为零)
静态 CSS 文件输出✅❌(多为运行时注入)✅

关键点在于文件分离:treat 把样式代码放进带特殊后缀的treat 文件(如Button.treat.js),使它们可以在构建时执行,而不是被打进浏览器 bundle。文件里导出的不再是「样式对象」,而是生成的类名:

// Button.treat.js 构建后等效于: export var button = 'GENERATED_CLASS_NAME';

原理详见官方文档 docs/how-it-works.md。

二、编译流程全景图

整个流程由 webpack 插件 TreatPlugin 驱动,可以概括为 6 步:

  1. 识别:test: /\.treat\.(js|ts)$/规则匹配 treat 文件,交给专用 loader;
  2. 预编译:用 webpack「子编译器」把 treat 文件在 Node 环境下打包成一段可执行脚本;
  3. 执行:在 Node 中执行脚本,通过 adapter 拦截样式 API 调用,收集样式与主题;
  4. 转 CSS:每块样式生成一个虚拟的.treatcss文件,走 css-loader → 输出 loader 管线;
  5. 序列化:把 treat 文件的导出替换为「类名字符串」模块;
  6. 后处理:重建过期模块、删除未使用的 CSS、修正样式顺序。

💡 一句话总结:treat 文件在构建期"消失",CSS 和类名在构建期"诞生"。

三、逐步拆解编译管线

步骤 1:识别 .treat 文件,接管编译

TreatPlugin 在apply阶段向 webpack 注入两条规则:一条匹配*.treat.(js|ts)走 treat loader;另一条匹配生成的*.treatcss文件,走css-loader+ 你配置的outputLoaders(默认style-loader,可换成 mini-css-extract-plugin 产出静态 CSS 文件),见 index.js#L379-L421。

loader 并不直接处理源码,而是在pitch阶段截获编译,核心逻辑集中在 produce 函数。

步骤 2:子编译器在 Node 中「预编译」treat 文件

compileTreatSource 会为每个 treat 文件创建一个child compiler(名称treat-webpack-loader):

  • 以NodeTargetPlugin为目标环境编译,产物是 CommonJS 脚本;
  • 用ExternalsPlugin('commonjs', 'treat')把treat包标记为外部依赖——真正执行时由 loader 注入实现;
  • 结果带fileDependencies(依赖了哪些文件),watch 模式下据此失效缓存;
  • 编译结果有内存缓存,改动文件只会失效相关条目,不会全部重算。

这样 treat 文件里可以写任意 JavaScript 逻辑(循环、计算、依赖其他工具函数),而不只是静态的样式声明。

步骤 3:执行脚本,adapter 拦截样式调用

loader.js#L175-L189 会在执行前给脚本「垫」一行代码:require('treat/adapter').setAdapter(__webpack_treat__)。

treat 库的所有样式 API(style、createTheme等)实际都经过 adapter 层转发。构建时注入的__webpack_treat__对象提供 5 个能力:

方法作用
addLocalCss收集非主题化样式块
addThemedCss按主题收集主题化样式块
addTheme/getThemes向全局 store 注册/读取主题
getIdentName依据localIdentName/themeIdentName生成类名(dev 可读、prod 哈希)

全局 store 负责记录「模块 ↔ 主题 ↔ CSS 请求」的三方关系,是后续去重、重建、裁剪的数据基础。

步骤 4:样式块 → 虚拟 .treatcss 文件

执行完毕后,loader 把收集到的每一块样式交给 processCss 处理(按browsers目标做兼容转换,生产模式自动 minify),然后:

  1. 把 CSS 内容 base64 编码,包成virtual-resource-loader虚拟资源;
  2. 按内容哈希生成*.treatcss文件名——相同 CSS 自动去重;
  3. 该文件匹配第二条 webpack 规则,进入css-loader与outputLoaders管线,最终由 mini-css-extract-plugin 等插件落成静态 CSS 文件。

也就是说,treat 生成的 CSS 与你手写的 CSS 走的是同一条 webpack 管线,享受同样的缓存、HMR 与提取能力。

步骤 5:导出被替换为「类名字符串」

serializeTreatModule 把执行结果序列化为一行行export var xxx = '...';形式的纯代码,并生成对.treatcss的 import。

这里有一道安全护栏:treat 文件只允许导出纯对象、数组、字符串、数字、null/undefined,其余类型直接报错——这保证了产物中绝不含样式逻辑。

步骤 6:编译后整理——重建、裁剪与排序

插件在finishModules、optimizeDependencies、afterChunks三个钩子中做收尾(index.js#L80-L362):

  • 主题哈希重建:每个 treat 模块记录编译时的主题集合哈希,主题文件晚加载导致样式不全的「过期模块」会被自动重建,保证每个主题都拿到完整 CSS;
  • 未用 CSS 裁剪:owner 模块或主题模块未被引用时,对应 CSS 模块的依赖会被移除,实现样式层面的 tree-shaking(测试覆盖见 tests/fixtures/unused-modules/);
  • 顺序修正:reIndexModules 重排模块与依赖的索引,确保 CSS 的文档顺序与 treat 文件的书写顺序一致,主题化样式保持在更高优先级位置。

四、主题化:一个主题生成一份 CSS

treat 的主题方案是「编译期展开」:为每个主题生成独立的一组类名,例如两个主题 + 一个text样式,构建出:

.text_greenTheme { color: green; } .text_redTheme { color: red; }

由此带来的两个规则值得记住:

  • 主题化样式优先级固定高于非主题化样式,与书写顺序无关;
  • 因此官方建议不要依赖跨类的样式覆盖,优先在单个 treat 文件内用条件逻辑控制样式。

主题还能通过 webpack 动态 import 做主题级 bundle 拆分,按需加载主题对应的 CSS。

五、运行时:一次查表而已

如果应用不用主题化,运行时完全不需要 treat——bundle 里只有类名字符串。

启用主题时,只需调用 resolveStyles(React 场景用useStylesHook):它根据主题引用,把样式对象深拷贝并把每个类名解析为该主题对应的生成类。由于导出结构是静态的,解析结果按「模块 + 主题」缓存在内存 Map 中,整个应用生命周期每个组合只解析一次。

⚡ 这就是「near-zero runtime」的来源:运行时不计算样式、不拼接选择器、不注入 CSS,只做一次字符串查表。

六、总结:构建期与运行期的分工

阶段发生的事对应源码
构建期识别.treat文件webpack-plugin/index.js
构建期子编译器 Node 化预编译treatCompiler.js
构建期执行 + adapter 收集样式/主题loader.js、adapter.ts
构建期CSS 生成、去重、提取processCss.js
构建期重建、裁剪、排序store.js
运行期按主题查表取类名(可选)resolveStyles.ts

理解了这条管线,你就能解释 treat 的几个标志性行为:为什么样式代码不会进 bundle、为什么 CSS 可以静态提取、为什么主题切换零成本。若想动手验证,可以阅读 docs/setup.md 的 webpack 配置说明,或参考官方示例 examples/next-treat-example/ 与测试夹具 packages/treat/tests/fixtures/ 中的 simple、themed、unused-modules 等场景。

【免费下载链接】treat

:candy: Themeable, statically extracted CSS‑in‑JS with near‑zero runtime.

项目地址:https://gitcode.com/gh_mirrors/tre/treat
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询