阅读 2196

[译] Atomic CSS-in-js

译者: 国内大家都去搞 JS 去了,可以看到我们有可以与 REACT 抗衡的 VUE。却显见有类似 CSS-in-js, OOCSS,BEM,Atomic CSS ... 的 CSS 解决方案出现?这是为什么呢?

随着 Facebook 和 Twitter 最近的产品技术方案的迭代更新,我们看到了一个新的流行趋势: Atomic CSS-in-JS。

在这篇文章中,我们将看到什么是 Atomic CSS,它是如何与像 TailwindCSS 这样的 functional / utility-first CSS 之类技术方案的关系,以及哪些基于 react 框架的大厂是如何使用它的。

因为我不是这方面的专家,所以不要指望深入了解它的优缺点。我只是希望你能从对它的了解中有所启发。

注意: Atomic CSS 与 Atomic Design 设计无关。

译者注:Atomic CSS 是 CSS 代码的一种设计模式,Atomic Design 是对于设计资源和设计组织方式的一种设计理念。

什么是 atomic CSS?

大家可能听说过各种 CSS 解决方案,例如BEM,OOCSS ...

<button class="button button--state-danger">
    Danger button
</button>
复制代码

如今,人们越来越喜欢像 Tailwind CSS 一样的 utility-first 概念。 这与 Functional CSS 和 Tachyon 很接近。

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>
复制代码

使用一组更小单元的 class 类,我们可以实现更多的效果

Atomic CSS 可以看作是 utility-first CSS 的一种极致抽象: 所有的 CSS 类都有且只有一个单一的,独特的 CSS 规则。Thierry Koblentz (Yahoo!) 在 2013年 的“ Challenging CSS Best Practices” 首次提到了 CSS。

/* Atomic CSS */
.bw-2x {
  border-width: 2px;
}
.bss {
  border-style: solid;
}
.sans {
  font-style: sans-serif;
}
.p-1x {
  padding: 10px;
}
/* Not atomic, because the class contains 2 rules */
.p-1x-sans {
  padding: 10px;
  font-style: sans-serif;
}
复制代码

我们不得不承认在使用 utility/atomic CSS 时,是将结构层和样式层耦合在了一起:当我们需要更改按钮颜色时,我们修改的是 HTML,而不是 CSS。 这种紧密耦合的方式在现代 CSS-in-JS React 代码库中也得到了认可,但这个现存的“关注点分离”的主流思是相违背的。

因为我们使用简单的类选择器,所以“关注点分离”也不再是什么大的问题。

我们现在通过结构来修改样式,会有以下几个特点:

  1. 随着我们添加新功能,样式表的膨胀率反而会减小
  2. 我们在移动 HTML 的时候就能同时修改我们的样式。
  3. 我们在删除一个组件的同时,就能确保我们删除了与之相关的样式。

当然这会增大 HTML 的体积。对于服务器渲染的 Web 应用程序,可能会是一个问题,但是利用 gzip 可以很好地压缩类名中的高冗余度,这和压缩 CSS 文件中重复 CSS 样式的方式一样。

你不需要在任何情况下都使用 utility/atomic CSS , 它更适合大多数通用的样式素材。

译者注:也就是 Design Token,Atomic CSS 在对接 Design Token 会发挥出它最大的优势

一旦定义好 utility/atomic CSS,它就很难改变或增长。 利用这个特性,我们就可以主动将它缓存(例如,把它添加到 vendor.css 中,并不需要每次都重新部署)。 它移植行也非常高,可以很方便的在其他应用程序中使用。

utility/atomic CSS 的局限性

Utility/atomic CSS 非常的棒, 但是它也有一些坑。

通常我们会在项目初期手动创建 utility/atomic CSS,并制定好命规则。 但这很难确保它的易用性,以及控制它的膨胀率。 这个能够在让多个人使用的情况下保持一致吗? 是否受bus factor因素影响?

译者注: bus factor 我猜测是指的代码里面的破窗效应。

让 Tailwind 来拯救我们

Tailwind 的方法非常方便,可以解决其中的一些问题。

它并没有真正为所有网站提供唯一的 utility CSS 文件。相反,它仅提供共享的作用域和命名约定。通过配置文件,您可以生成属于自己的utility CSS。

Tailwind的知识可以移植到其他应用程序,即使它们没有使用完全相同的类名。这让我想起了React的“学习一次,随处写”的哲学。

我见过有人提到说,Tailwind 的类,几乎能满足了他们日常 90% 或 95% 的样式需求。这个覆盖面已经足够大了,我们通常不需要使用一次性样式。

基于这个点您可能会想知道为什么要用 Atomic CSS 来替代 Tailwind 呢?

在严格遵守 Atomic CSS 中一个 Class 只有一条规则的逻辑下你会得到什么?您最终将获得更大的 HTML 标记,和不方便的命名约定?不管怎么样,Tailwind 已经内置了许多原子类。

因此,我们应该放弃 Atomic CSS 的想法,而选择看起来更简单的 Tailwind ?

Tailwind 是一个非常棒的解决方案,但是仍然存在一些尚未解决的问题:

  • 需要学习有针对性的命名约定
  • CSS规则插入顺序仍然很重要
  • 可以轻松删除未使用的规则吗?
  • 我们如何处理其余的一次性样式?

与 Tailwind 相比,手写 Atomic CSS 样式并不是一个好的方案。

和 CSS-in-JS 做比较

CSS-in-JS, and utility/atomic CSS 还是有很多共同点的。两种方法都主张从标记中进行样式化,类似写内联样式,这使它们具有许多相似的属性(例如,可以放心地移动内容)。

Christopher Chedeau 极大地帮助了在 React 生态系统中传播 CSS-in-JS 的想法。 在多次talks中,他解释了CSS的问题:

Problems with CSS at Scale

Utility/atomic CSS CSS-in-JS / atomic CSS 解决了其中的一些问题,但显然不是全部(特别是样式的不确定性解析)。

如果它们具有如此多的相似性,我们不能同时使用它们吗?

走进 Atomic CSS-in-JS

Atomic CSS-in-JS 可以看作是 “自动化的 atomic CSS”:

  • 您无需再创建 CSS 类名
  • 普通样式和一次性样式的处理方式相同
  • 能够提取页面的关键 CSS ,并进行代码拆分
  • 有机会解决 JS 中 CSS 规则插入顺序的问题

我不知道目前所有 CSS-in-JS 库对于 Atomic css 的支持情况。 支持它实际上是 CSS-in-JS 库的实现细节。支持情况可能是有的有,有的没有,或者是可选项。

我将重点推荐以下两个解决方案,这导致最近出现了两个大规模的原子CSS-in-JS 的使用:

同样的还有: Styletron, Fela, cxs

React-Native-Web

React-Native-Web 是一个非常有趣的库:它允许在 Web 上渲染 React-Native。 这里我们并不是真正在谈论跨平台的 移动/ Web开发(请从论坛以获取更多详细信息)。

作为一名Web开发人员,你只需要了解 React-Native-Web 是一个常规的 CSS-in-JS 库,带有一小部分原始的 React 组件。 无论您在何处看到 View,都可以将它替换为div,这是您的最佳选择。

React-Native-Web 由 Nicolas Gallagher 创建,致力于 Twitter 移动端。 他们逐步将其部署到移动设备上,不确定确切时间,但可能在 2017/2018 年左右。 从那时起,它已被其他公司(大联盟足球,Flipkart,Uber,泰晤士河…)使用,但最重要的部署是 Paul Armstrong 领导的团队开发的新的 2019 Twitter 桌面应用程序。

Stylex

Stylex 是在 Facebook 上开发的新 CSS-in-JS 库,用于 2020 年 Facebook 重写(当前为beta)。 看来他们计划有朝一日将其开源,可能使用不同的名称。

值得一提的是,React-Native-Web 的作者 Nicolas Gallagher 是 2 年前被 Facebook 聘用的。 看到它的某些概念被 Facebook 重用也就不足为奇了。

与 React-Native-Web 不同,Stylex 似乎并不专注于跨平台开发。

我收到的所有信息都来自论坛 :) 我们将不得不等待更多详细信息。

膨胀率

正如 Atomic CSS 所期望的那样,Twitter 和 Facebook 的 CSS 都已经大幅减少了,他们都遵循图中的曲线。 虽然还是需要为一些应用付出努力。

Facebook给到的具体数字:

  • 他们的旧网站只是着陆页就有 413Kb 的CSS
  • 他们的新站点整个站点为 74Kb,包括黑暗模式

资源和输出

这两个库似乎有一个相似且相当简单的API,但是很难说,因为我们对 Stylex了解不多。

值得强调的是,React-Native-Web 会处理 CSS 缩写 和 margin: 0;这样的语法。

生产环境

让我们看看 Twitter 上的 HTML 是什么样的:

再看看新的 Facebook:

很多人看到这个可能会吓一跳,但不得不承认它确实有效,而且仍然可以使用。

在 Chrome 检查器中浏览样式可能有点困难,但 devtools 可以帮助:

CSS rules 执行顺序

和手写 utility/atomic CSS 不一样的是,JS 库能够使样式不依赖于 CSS 规则的插入顺序。我们都知道,在规则冲突的情况下,获胜的不是 class 属性的最后一个类,而是在样式表中最后插入的规则。 我们只能通过使用简单的基于类的选择器来解决特异性问题。

实际上,这些库避免在同一元素上输出具有冲突规则的类。 他们确保标记中声明的最后一个样式始终获胜。 “覆盖的类”已被过滤,甚至没有进入 DOM。

const styles = pseudoLib.create({
  red: {color: "red"},
  blue: {color: "blue"},
});
// That div only will have a single atomic class (not 2!), for the blue color
<div style={[styles.red, styles.blue]}>
  Always blue!
</div>
// That div only will have a single atomic class (not 2!), for the red color
<div style={[styles.blue, styles.red]}>
  Always red!
</div>
复制代码

如果一个类有多个规则,而其中只有一个被覆盖,则 CSS-in-JS 库将无法过滤该类而不删除非覆盖的规则。

如果一个 class 有一条简单的缩写规则,例如 margin:0,但是覆盖它的样式是 marginTop:10,会遇到同样的问题。 如果 margin:0 的简写语法被扩展为 4 个不同的类(marginTop:0;marginLeft:0; marginRight:0; marginBottom:0),这些库就能够过滤出不应出现在 DOM 中被覆盖的类。

你还是喜欢 Tailwind?

一旦了解了所有 Tailwind 命名约定,就可以非常快速地编写 UI。就很难再回到类似 CSS-in-JS 那样手工一行行去写样式的方式。

没有什么可以阻止你在Atomic CSS-in-JS 框架之上构建自己的抽象。 Styled-system可以让很多 CSS-in-JS 的库支持 Atomic CSS。如果你还是更喜欢 Tailwind 的方式,你甚至可以改写 Tailwind 在 JS 中的命名规则。

这是传统 Tailwind 的写法:

<div className="absolute inset-0 p-4 bg-blue-500" />
复制代码

我们随便找一个方案(react-native-web-tailwindcss) 我在goole 上看到的。

import {t} from 'react-native-tailwindcss';
<View style={[t.absolute, t.inset0, t.p4, t.bgBlue500]} />
复制代码

就生产率而言,这并没有太大不同。 而且您可以使用 TypeScript 规避一些输入错误的问题。

总结

关于Atomic CSS-in-JS,我想说的就这些。

我从未在任何大型生产环境项目中使用过Atoatomic CSS, atomic CSS-in-JS 或着 Tailwind 。 我可能在某些地方错了,请随时在Twitter上纠正我。

我认为原子CSS-in-JS在React生态系统中是一种趋势,我希望您从这篇文章中学到了有用的东西。

由于我还没有找到任何关于原子CSS-in-JS的文章,所以这篇文章主要是为我自己写的。当我在以后的博客文章中提到原子CSS-in-JS时,我希望有一个资源链接(我计划撰写更多关于React-Native-Web和跨平台的文章,敬请关注)。

感谢您的阅读。

译者的话

Atomic CSS 我不知道已经推荐了多少次了,这么说吧它是目前现存的方案中,能解决 CSS 难题最多的解决方案没有之一。特别是在基于组件化思维的react 或者 vue 项目中使用。

  1. 我说 CSS 类名可以缩写,你不要打我!
  2. 如何管理 CSS “内裤”
  3. 「CSS思维」组件化VS原子化

另外再推荐一个自己多年 Atomic CSS 使用下来的经验总结的一个 npm 库。

名称 NPM github
@_nu/css-acss
npm package
github

可以把@_nu/css-acss 理解为阉割版的 Tailwind。用阉割来换取上手成本使用体验。五分钟看完文档你就能理解所有的逻辑。

个人能力和翻译水平有限,有错误欢迎指正