CSS-in-JS 走过了一段曲折的路。从 styled-components 的模板字面量魔法,到 Tailwind 的原子化横扫一切,再到 React Server Components 对运行时 CSS-in-JS 的"死刑判决"——前端样式方案几乎每两年就换一套范式。

但有一个生态位一直被忽视:React Native 的 StyleSheet.create。它不仅活过了所有 Web 端的样式框架迭代,还默默提供了类型安全、零运行时成本、声明式 API 的最佳组合。本文将这种思维引入 Web React 项目。

CSS-in-JS 的演进与困境

回顾一下这条路的几个关键节点:

每个方案都在"组件封装性"和"运行时成本"之间做取舍。而 React Native 的 StyleSheet 恰好同时满足两者——它既不是运行时方案,也不依赖类名字符串拼接。

StyleSheet.create 的核心理念

React Native 的样式系统有几个关键设计决策值得学习:

一个 useStyles hook 的实现

将这种模式移植到 Web 端并不复杂。核心思路是:用 TypeScript 的泛型约束确保样式对象的类型安全,同时保持 API 与 React Native 一致。

import { useMemo } from 'react';

type Styles<T> = { readonly [K in keyof T]: React.CSSProperties };

function createStyles<T extends Record<string, React.CSSProperties>>(
  styles: T
): Styles<T> {
  return Object.freeze(styles) as Styles<T>;
}

function useStyles<T extends Record<string, React.CSSProperties>>(
  factory: () => T
): Styles<T> {
  return useMemo(() => createStyles(factory()), []);
}

// 使用示例
const useCardStyles = () => useStyles(() => ({
  container: {
    display: 'flex',
    padding: '24px',
    borderRadius: '10px',
    background: 'var(--bg-card)',
    border: '1px solid var(--border)',
  },
  title: {
    fontSize: '18px',
    fontWeight: 600,
    color: 'var(--text)',
  },
}));

function Card({ title }: { title: string }) {
  const s = useCardStyles();
  return (
    <div style={s.container}>
      <h3 style={s.title}>{title}</h3>
    </div>
  );
}

相比 Tailwind 的 className="flex p-6 rounded-xl bg-gray-800 border border-gray-700",StyleSheet 模式的优势在于:样式被赋予语义化名称(container / title),而不是视觉属性的机械罗列。当设计系统需要调整间距或圆角时,只需修改一处定义,所有消费点自动更新。

与 React Server Components 的兼容性

RSC 的核心约束是不能使用 hooks 和浏览器 API。而 useStyles 底层依赖 useMemo,自然无法在 Server Component 中运行。解决方案有两种:

这种分层策略让我们在不同场景中选择最优方案:需要交互的组件享受类型安全的动态样式,纯展示组件获得零 JS bundle 的 RSC 优势。

实际项目中的迁移体验

在一个中等规模的内部组件库(约 40 个组件)中,我们从 Tailwind 切换到这套方案后:

当然也有取舍:从工具链角度,失去了 Tailwind 的 tree-shaking 和 JIT 编译优势。但对于组件库这种样式定义集中、重用率高的场景,手动管理 CSS 变量的方案已经足够高效。


React Native 的 StyleSheet 不是一个"移动端的妥协方案",而是一套经过千万级应用验证的组件样式方法论。在 React 生态被 Tailwind 全面笼罩的今天,重新审视这套 API 的设计哲学,或许能帮我们跳出"类名 vs CSS-in-JS"的二元对立,找到第三条路。