- 原文:Atomic CSS-in-JS
- 时间:2020/04/27
- 作者:Sébastien Lorber
- 译者:ziven27
- 翻译时间:2020/05/10
译者: 国内大家都去搞 JS 去了,可以看到我们有可以与 REACT 抗衡的 VUE。却显见有类似 CSS-in-js, OOCSS,BEM,Atomic CSS ... 的 CSS 解决方案出现?这是为什么呢?
随着 Facebook 和 Twitter 最近的产品技术方案的迭代更新,我们看到了一个新的流行趋势: Atomic CSS-in-JS。
在这篇文章中,我们将看到什么是原子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>
复制代码
使用一组实用程序类的样式表,我们其实可以做得更多。
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 代码库中也得到了认可,但这个现存的“关注点分离”的主流思是相违背的。
因为我们使用简单的类选择器,所以“关注点分离”也不再是什么大的问题。
我们现在通过结构来修改样式,会有以下几个特点:
- 随着我们添加新功能,样式表的膨胀率反而会减小
- 我们在移动 HTML 的时候就能同时修改我们的样式。
- 我们在删除一个组件的同时,就能确保我们删除了与之相关的样式。
当然这会增大 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的问题:
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 的使用:
- React-Native-Web at Twitter (more details in Nicolas Gallagher’s talk)
- Stylex at Facebook (more details in Frank Yan’s talk)
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 那样手工一行行去写样式的方式。
Nothing prevents you for building your own abstractions on top of an atomic CSS-in-JS framework. Styled-system might be able to run some of the CSS-in-JS libraries supporting atomic CSS. It’s even possible to reuse naming conventions for Tailwind in JS, if you feel you are productive with it.
没有什么可以阻止你在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 项目中使用。
另外再推荐一个自己多年 Atomic CSS 使用下来的经验总结的一个 npm 库。
名称 | NPM | github |
---|---|---|
@_nu/css-acss |
可以把@_nu/css-acss
理解为阉割版的 Tailwind。用阉割来换取上手成本和使用体验。五分钟看完文档你就能理解所有的逻辑。
个人能力和翻译水平有限,有错误欢迎指正
来源:oschina
链接:https://my.oschina.net/u/4281386/blog/4274833