告别运行时 JS 中的 CSS:服务器组件与构建时样式
为什么运行时 JS 中的 CSS 会与 React Server Components 及性能目标产生冲突,Tailwind、CSS Modules 和零运行时库有何差异,以及如何安全地进行迁移。
来源: Why CSS-in-JS Is Dying in 2026: Performance, Server Components, and What to Use Instead
多年来,styled-components和Emotion这类运行时CSS-in-JS库一直是为React组件添加样式的首选方案:样式局限于单个组件,数值由属性驱动,且不存在全局类名冲突的问题。此后,受性能要求以及React Server Components的推动,该领域逐渐转向在构建时生成样式的方案。本文将解释这些变化,对比主要的替代方案,并为读者提供实用准则,帮助判断何时应该迁移现有代码库,何时无需改动。
无论规模大小,许多团队都已开始放弃运行时样式方案来处理新项目,在选择具体方法之前,了解其背后的原因十分重要。
在浏览器中生成样式的成本
运行时 JS 中的 CSS 库会在应用程序运行期间生成 CSS。当组件被渲染时,该库会将样式定义(包括任何依赖于属性的值)转换为 CSS 规则,生成类名,并将 <style> 标签注入到文档中。这些操作都在用户设备的主线程上执行。已有对比静态 CSS 的测试结果显示,渲染速度会下降 20% 到 50% 不等;实际影响因应用程序而异,因此应将这类数据视为测量自身页面性能的参考,而非通用标准。
根本问题在于架构设计,而非某个特定库的缺陷:样式计算是在浏览器中进行的,而非在构建阶段。这意味着样式引擎本身会包含在你的 JavaScript 包中,浏览器需要在页面具备交互功能之前解析并执行更多脚本,而且样式插入还会与渲染过程争夺时间。
为何服务器组件暴露了这一弱点
React 服务器组件(RSC)在服务器端进行渲染,然后将结果发送到浏览器,而不会将组件代码作为客户端 JavaScript 发送。运行时的 CSS-in-JS 方案依赖于能够在渲染过程中跟踪并注入样式的 JavaScript 运行时,同时还依赖 React 上下文,而服务器组件并不支持这些功能。这就使得它与 Next.js App Router 等框架所采用的服务器优先模型相冲突。
在实际应用中,若要在服务端组件中使用 styled-components 或 Emotion,就需要通过 'use client' 指令将组件树中需要样式处理的部分标记为客户端组件,通常还需额外配置以在服务端渲染时收集样式。当到处都使用带样式的基础组件时,大量组件树内容最终会传输到客户端。这反而抵消了服务端组件的诸多优势,比如减少发送到浏览器的 JavaScript 代码量以及降低浏览器端的处理负担,而这一切只是为了保证样式库能够正常运行。对许多团队而言,这种权衡并不值得。
构建时的替代方案
运行时 CSS-in-JS 技术在 2018 年至 2023 年间达到巅峰。如今已有几种成熟的方案可以避免运行时开销,每种方案都适合不同的团队需求。
Tailwind CSS 作为通用默认选择
Tailwind CSS 已成为新创建的 React 和 Next.js 项目的常用起点。你可以在标记语中直接编写实用类,构建阶段只会输出实际使用的类的 CSS 代码。由于没有运行时样式处理机制,生成的样式表体积很小,而且因为输出的是纯 CSS,所以无需任何特殊处理即可在服务器组件中使用。
适合偏好纯 CSS 的团队的 CSS 模块
如果你更愿意编写普通 CSS 而非实用类,CSS 模块可以为你提供局部作用域的类名,且不会带来任何运行时开销。大多数打包工具和框架技术栈都原生支持 CSS 模块,因此通常无需进行任何配置即可使用。
为熟悉的传统开发模式提供的零运行时 CSS-in-JS 方案
Panda CSS和Vanilla Extract这类工具允许你像使用styled-components时一样,继续在JavaScript或TypeScript中编写样式,但它们会在构建过程中将所有内容转换为静态CSS。这样既能保留原有的开发体验和类型安全,又没有运行时开销,同时还与Server Components完全兼容。主要的调整在于样式必须能够被静态分析,因此那些在运行时会变化的值通常通过variants或CSS自定义属性来表示,而非任意的props。关于如何将JavaScript值转换为CSS变量的方法,可在我们关于运行时CSS自定义属性的指南中找到。
决定何时进行迁移
新项目
对于新项目而言,如今几乎没有理由选择运行时 CSS-in-JS 方案。合理的默认选择是:
- 大多数项目使用 Tailwind CSS。
- 如果团队更喜欢传统样式表,则选择 CSS Modules。
- 若希望继续在 TypeScript 中编写样式而不需承担运行时成本,可选择零运行时的 CSS-in-JS 库。
现有项目
对于已经基于 styled-components 或 Emotion 构建的应用,无需急于迁移。这两个库在客户端渲染应用中依然表现良好。只有当您打算采用 Server Components,或性能分析显示样式是渲染成本中的重要组成部分时,才值得考虑迁移方案。
进行迁移时应当逐步进行,一次处理一个组件,逐步过渡到 Tailwind 或 CSS Modules。叶子组件以及按钮之类的共享基础元素是理想的初始迁移目标。在过渡期间这两种系统可以共存,但在旧样式与新样式并存时需注意样式的优先级和顺序。
总结
- 运行时 CSS-in-JS 确实解决了实际问题,但它会在客户端计算样式,这会增加包体积并占用主线程时间。
- 服务端组件使得这种成本更加明显,因为相关库依赖客户端 JavaScript,并将带样式的结构推回客户端。
- Tailwind CSS、CSS Modules 以及 Panda CSS 和 Vanilla Extract 等无运行时开销的库都会在构建阶段生成静态 CSS,能够与服务端组件完美配合使用。