背景
本文将向你展示 TailwindCSS 如何通过在特定场景下更好地组织代码,真正提升我们的生产力和项目的可维护性。
性能影响
TailwindCSS 会如何影响我们网站的性能?
这是大多数开发者关心的关键问题,但这不是本文要讨论的主要话题。更多信息请查看控制文件大小 - Tailwind CSS 官方文档。该文档的基本结论是,引入 TailwindCSS 很难产生超过 10kb 的压缩 CSS。
CSS 的设计
CSS 的设计受到了 HTML 设计的影响。CSS 在 HTML 诞生时就出现了,并已使用超过 20 年。我们知道,在 React/Vue 等库/框架出现之前,网站被分为 HTML、CSS 和 JavaScript。网站页面没有模块化的概念。每个网站页面都写在单个 HTML/CSS/JavaScript 文件中。
为了提供一种抽象手段来复用样式,CSS 提供了 class 来帮助开发者组合样式并在不同地方复用它们。
1 | // Without CSS Class feature |
React 带来了模块化,使 CSS 类变得多余
React 开始在网站中使用模块化概念,将网站视为页面的组合,而页面是组件的组合。每个组件都有自己的 HTML 结构(JSX)、样式(CSS)和逻辑(JS),可以作为模块复用。
上面的例子在 React 中是这样的:
1 | const HighlightP = ({ children }) => (<p class="highlight">{children}</p>) |
我们很快发现,CSS 提供的 class 是多余的,因为 React 已经包含了样式在内的模块化:
1 | const HighlightP = ({ children }) => (<p style={{ color: 'red', fontWeight: 'bold' }}>{children}</p>) |
这样做,我们减少了从 CSS class 到 React 组件的映射。这就是 TailwindCSS 的基本概念。
你可能会问 CSS class 有什么问题?让我们再多做一些比较:
- 命名很难。 通过写 CSS class,你必须为每个想要添加样式的节点起一个名字。这是在浪费精力。
- 同一组件的代码分散各处。 如果你写 CSS class,你需要在两个地方放置组件代码,可读性和可维护性下降。例如,删除代码时,你可能忘记删除对应的 CSS class。
- 避免 Bug。 CSS 是全局的,当你进行修改时,可能会意外地覆盖某些样式。
使用 TailwindCSS 替代内联样式
TailwindCSS 提供了比内联样式更好的功能:
- 在约束下进行设计。使用内联样式时,每个值都是一个魔法数字。使用工具类,你从预定义的设计系统中选择样式,这使得构建视觉上一致的 UI 更加容易。
1 | /** @type {import('tailwindcss').Config} */ |
- 响应式设计。你不能在内联样式中使用媒体查询,但你可以使用 Tailwind 的响应式工具来轻松构建完全响应式的界面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">
// CSS
@media (min-width: 640px) { ... }
5. Hover、focus 及其他状态。内联样式无法针对 hover 或 focus 等状态,但 Tailwind 的[状态变体](https://tailwindcss.com/docs/hover-focus-and-other-states)使使用工具类轻松为这些状态设置样式成为可能。
<button class="bg-sky-500 hover:bg-sky-700 ...">
Save changes
</button>
// CSS
.btn-primary {
background-color: #0ea5e9;
}
.btn-primary:hover {
background-color: #0369a1;
}
StyleX 简介
Meta 最近开源了一个新的 CSS 框架 StyleX。它提供了一种与 TailwindCSS 不同的 CSS 代码组织方式。
我做了一些调研,想在这里分享 StyleX 的核心理念。
OOCSS(面向对象 CSS)约定
StyleX 认为工具类让 HTML 标记的可读性变差。因此他们仍然使用传统的 OOCSS 作为组织代码的方式。
1 | import * as stylex from '@stylexjs/stylex'; |
同时,它通过使用 CSS-in-JS、TypeScript 和编译,设法解决了 OOCSS 架构的一些问题:
- 模块化:传统 OOCSS 是全局的,模块化程度差。StyleX 将样式定义为组件 JS 代码中的标记,使其局部化。
- 类型安全的 CSS:通过使用 TypeScript,StyleX 像组件的其他 props 一样是类型安全的。
1 | type alignContent = |
- 避免 CSS 膨胀(?):根据官方文档,StyleX 不仅在合并多个选择器时,而且在合并多个简写和全称属性时(例如 margin vs margin-top),都能产生确定性的结果。
但当我在本地尝试时,它并没有成功合并任何内容。
1 | const s = stylex.create({ |
结论
- 性能:随着代码库中 class 的增长,性能会变低,但如果是小项目,它可能会优于 TailwindCSS,因为 TailwindCSS 会导入不必要的工具类,而 StyleX 只导入我们在代码中编写的内容。
- 可维护性和可读性:工具类(TailwindCSS)有其优势,但会让 HTML 标记变得难看;StyleX 解决了 OOCSS 的一些问题,但仍然留下了命名和 class 膨胀未解决的问题。