零运行时 CSS-in-JS:兼顾开发体验与极致性能

5 min

1. CSS-in-JS 的“双刃剑”

CSS-in-JS 方案,如 styled-componentsEmotion,通过允许开发者在 JavaScript/TypeScript 文件中编写 CSS,彻底改变了组件化开发的样式管理方式。它带来了许多好处:

  • 组件作用域: 样式默认与组件绑定,解决了 CSS 全局命名冲突的问题。
  • 动态样式: 可以基于组件的 props 或 state 轻松创建动态样式。
  • 代码共存: 样式和组件逻辑放在同一个文件中,便于维护和组织。

然而,这些便利性并非没有代价。传统 CSS-in-JS 库的核心是 运行时 (Runtime)。当组件在浏览器中挂载时,CSS-in-JS 库的 JavaScript 运行时会:

  1. 解析模板字符串或对象中的 CSS。
  2. 生成唯一的类名。
  3. 将样式动态地注入到文档的 <head> 中的 <style> 标签里。

这个过程会增加 JavaScript 的包体积,并在应用启动和渲染时带来一定的计算开销,影响性能。

2. “零运行时”的破局之道

为了解决运行时性能问题,社区提出了一种新的思路:零运行时 (Zero-Runtime) CSS-in-JS

其核心思想是:保留 CSS-in-JS 的开发体验,但在 构建时 (build time) 就完成所有工作。构建工具(通常是一个 Babel 插件或 Vite 插件)会扫描你的代码,找到所有 CSS-in-JS 的用法,然后:

  1. 将 CSS 文本提取出来。
  2. 生成静态的 .css 文件。
  3. 将原始的 CSS-in-JS 代码替换为生成的、唯一的类名。

最终,交付给浏览器的 JavaScript 包中 不包含任何 CSS-in-JS 的运行时库,其效果等同于手写的高度优化的静态 CSS 文件。

3. 主流零运行时方案

a. Linaria

Linaria 是零运行时领域的先驱之一。它允许你使用熟悉的 styled 标签模板字符串语法。

// YourComponent.js
import { styled } from '@linaria/react';

const Title = styled.h1`
  font-size: 2rem;
  color: tomato;
`;

// 构建时,这会被转换为:
// <h1 class="Title_a1b2c3d">...</h1>
//
// 并在一个静态 .css 文件中生成:
// .Title_a1b2c3d {
//   font-size: 2rem;
//   color: tomato;
// }

b. vanilla-extract

由 SEEK 公司团队开发的 vanilla-extract 更进一步,它利用 TypeScript 来创造完全类型安全的样式。所有样式定义都是 .css.ts 文件中的导出变量,提供了强大的类型推断和自动补全。

// styles.css.ts
import { style } from '@vanilla-extract/css';

export const title = style({
  fontSize: '2rem',
  color: 'tomato',
});

c. Panda CSS

Panda CSS 是一个较新的竞争者,它借鉴了 Tailwind CSS 的思想,提供了一种“样式属性” (Style Props) 的开发体验,同时保证了零运行时的输出。

import { css } from '../styled-system/css';

function MyComponent() {
  return <div className={css({ fontSize: '2rem', color: 'tomato' })} />;
}

4. 优势与权衡

优势:

  • 极致性能: 最终产物是静态 CSS,没有 JavaScript 运行时开销。
  • 更小的包体积: 无需在客户端加载 CSS-in-JS 库。
  • 强大的开发体验: 依然享受组件作用域、TypeScript 类型安全和代码共存的好处。

权衡:

  • 构建时依赖: 必须集成到构建流程中,配置相对复杂一些。
  • 动态性受限: 无法根据纯运行时的值(例如,来自 API 的数据)来生成全新的样式。不过,可以通过 CSS 变量等技术来弥补大部分动态场景。

结论

零运行时 CSS-in-JS 是对传统 CSS-in-JS 范式的一次“拨乱反正”。它巧妙地分离了“开发时体验”和“运行时性能”,让我们不再需要在两者之间做出艰难的妥协。对于追求极致性能和更小打包体积的现代 Web 应用来说,它提供了一个近乎完美的解决方案。