随着 Facebook 和 Twitter 最近的产品部署,我认为一个新的趋势正在缓慢增长:Atomic CSS-in-JS

在这篇文章中,我们将看到什么是Atomic CSS(原子 CSS),它如何与 Tailwind CSS 这种实用工具优先的样式库联系起来,目前很多大公司在 React 代码仓库中使用它们。

由于我不是这方面的专家,所以我不会去深入探讨它的利弊。我只是希望能帮助你了解它的大致内容。

先抛出一个令人开心的结论,新的 CSS 编写和构建方式让 Facebook 的主页减少了 80% 的 CSS 体积

什么是原子 CSS?

你可能听说过各种 CSS 方法,如 BEM, OOCSS…

<button class="button button--state-danger">Danger button</button>

现在,人们真的很喜欢 Tailwind CSS 和它的 实用工具优先(utility-first) 的概念。这与 Functional CSS 和 Tachyon 这个库的理念非常接近。

<buttonclass="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>Button
</button>

用海量的实用工具类(utility classes)组成的样式表,让我们可以在网页里大显身手。

原子 CSS 就像是实用工具优先(utility-first)CSS 的一个极端版本: 所有 CSS 类都有一个唯一的 CSS 规则。原子 CSS 最初是由 Thierry Koblentz (Yahoo!)在 2013 年挑战 CSS 最佳实践时使用的。

/* 原子 CSS */
.bw-2x {border-width: 2px;
}
.bss {border-style: solid;
}
.sans {font-style: sans-serif;
}
.p-1x {padding: 10px;
}
/* 不是原子 CSS 因为这个类包含了两个规则 */
.p-1x-sans {padding: 10px;font-style: sans-serif;
}

使用实用工具/原子 CSS,我们可以把结构层和表示层结合起来:当我们需要改变按钮颜色时,我们直接修改 HTML,而不是 CSS!

这种紧密耦合在现代 CSS-in-JS 的 React 代码库中也得到了承认,但似乎 是 CSS 世界里最先对传统的关注点分离有一些异议。

CSS 权重也不是什么问题,因为我们使用的是最简单的类选择器。

我们现在通过 html 标签来添加样式,发现了一些有趣的事儿:

  • 我们增加新功能的时候,样式表的增长减缓了。
  • 我们可以到处移动 html 标签,并且能确保样式也同样生效。
  • 我们可以删除新特性,并且确保样式也同时被删掉了。

可以肯定的缺点是,html 有点臃肿。对于服务器渲染的 web 应用程序来说可能是个缺点,但是类名中的高冗余使得 gzip 可以压缩得很好。同时它可以很好地处理之前重复的 css 规则。

一旦你的实用工具/原子 CSS 准备好了,它将不会有太大的变化或增长。可以更有效地缓存它(你可以将它附加到 vendor.css 中,重新部署的时候它也不会失效)。它还具有相当好的可移植性,可以在任意其他应用程序中使用。

实用工具/原子 CSS 的限制

实用工具/原子 CSS 看起来很有趣,但它们也带来了一些挑战。

人们通常手工编写实用工具/原子 CSS,精心制定命名约定。但是很难保证这个约定易于使用、保持一致性,而且不会随着时间的推移而变得臃肿。

这个 CSS 可以团队协作开发并保持一致性吗?它受巴士因子的影响吗?

巴士系数是软件开发中关于软件专案成员之间讯息与能力集中、未被共享的衡量指标,也有些人称作“货车因子”、“卡车因子”(lottery factor/truck factor)。一个专案或计划至少失去若干关键成员的参与(“被巴士撞了”,指代职业和生活方式变动、婚育、意外伤亡等任意导致缺席的缘由)即导致专案陷入混乱、瘫痪而无法存续时,这些成员的数量即为巴士系数。

你还需要预先开发好一个不错的实用工具/原子样式表,然后才能开始开发新功能。

如果实用工具/原子 CSS 是由别人制作的,你将不得不首先学习类命名约定(即使你知道 CSS 的一切)。这种约定是有主观性的,很可能你不喜欢它。

有时,你需要一些额外的 CSS,而实用工具/原子 CSS 并不提供这些 CSS。没有约定好的方法来提供这些一次性样式。

Tailwind 赶来支援

Tailwind 使用的方法是非常便捷的,并且解决了上述一些问题。

它通过 Utility-First 的理念来解决 CSS 的一些缺点,通过抽象出一组类名 -> 原子功能的集合,来避免你为每个 div 都写一个专有的 class,然后整个网站重复写很多重复的样式。

传统卡片样式写法:

Tailwind 卡片样式写法:

它并不是真的为所有网站提供一些唯一的实用工具 CSS,取而代之的是,它提供了一些公用的命名约定。通过一个配置文件,你可以为你的网站生成一套专属的实用工具 CSS。

ssh 注:这里原作者没有深入介绍,为什么说是一套命名约定呢而不是生成一些定死的 CSS 呢?

举几个例子让大家感受一些,Tailwind 提供了一套强大的构建系统,比如默认情况下它提供了一些响应式的断点值:

// tailwind.config.js
module.exports = {theme: {screens: {'sm': '640px',// => @media (min-width: 640px) { ... }'md': '768px',// => @media (min-width: 768px) { ... }'lg': '1024px',// => @media (min-width: 1024px) { ... }'xl': '1280px',// => @media (min-width: 1280px) { ... }}}
}

你可以随时在配置文件中更改这些断点,比如你所需要的小屏幕 sm 可能指的是更小的 320px,那么你想要在小屏幕时候采用 flex 布局,还是照常写 sm:flex,遵循同样的约定,只是这个 sm 已经被你修改成适合于项目需求的值了。

在比如说,Tailwind 里的 spacing 掌管了 margin、padding、width 等各个属性里的代表空间占用的值,默认是采用了 rem 单位,当你在配置里这样覆写后:

// tailwind.config.js
module.exports = {theme: {spacing: {'1': '8px','2': '12px','3': '16px','4': '24px','5': '32px','6': '48px',}}
}

你再去写 h-6(height), m-2(margin), mb-4(margin-bottom),后面数字的意义就被你改变了。

也许从桌面端换到移动端项目,这个 6 代表的含义变成了 6rem,但是这套约定却深深的印在你的脑海里,成为你知识的一部分了。

Tailwind 的知识可以迁移到其他应用程序,即使它们使用的类名并不完全相同。这让我想起了 React 的「一次学习,到处编写」理念。

我看到一些用户反馈说,Tailwind 提供的类名能覆盖他们 90% - 95% 的需求。这个覆盖面似乎已经足够广了,并不需要经常写一次性的 CSS 了。

此时,你可能想知道为什么要使用原子 CSS 而不是 Tailwind CSS?强制执行原子 CSS 规则的一个规则,一个类名,有什么好处?你最终会得到更大的 html 标签和更烦人的命名约定吗?Tailwind 已经有了足够多的原子类了啊。

那么,我们是否应该放弃原子 CSS 的想法,而仅仅使用 Tailwind CSS?

Tailwind 是一个优秀的解决方案,但仍然有一些问题没有解决:

  • 需要学习一套主观的命名约定

  • CSS 规则插入顺序仍然很重要

  • 未使用的规则可以轻松删除吗?

  • 我们如何处理剩下的一次性样式?

与 Tailwind 相比,手写原子 CSS 可能不是最方便的。

和 CSS-in-JS 比较

CSS-in-JS 和实用工具/原子 CSS 有密切关系。这两种方法都提倡使用标签进行样式化。以某种方式试图模仿内联样式,这让它们有了很多相似的特性(比如在移动某些功能的时候更有信心)。

Christopher Chedeau 一直致力于推广 React 生态系统中 CSS-in-JS 理念。在很多次演讲中,他都解释了 CSS 的问题:

  1. 全局命名空间
  2. 依赖
  3. 无用代码消除
  4. 代码压缩
  5. 共享常量
  6. 非确定性(Non-Deterministic)解析
  7. 隔离

实用工具/原子 CSS 也解决了其中的一些问题,但也确实没法解决所有问题(特别是样式的非确定性解析)。

如果它们有很多相似之处,那我们能否同时使用它们呢?

探索原子 CSS-in-JS

原子 CSS-in-JS 可以被视为是“自动化的原子 CSS”:

  • 你不再需要创建一个 class 类名约定

  • 通用样式和一次性样式的处理方式是一样的

  • 能够提取页面所需要的的关键 CSS,并进行代码拆分

  • 有机会修复 JS 中 CSS 规则插入顺序的问题

我想强调两个特定的解决方案,它们最近推动了两个大规模的原子 CSS-in-JS 的部署使用,来源于以下两个演讲。

  • React-Native-Web at Twitter (更多细节在Nicolas Gallagher 的演讲)。

  • Stylex at Facebook (更多细节在Frank Yan 的演讲)。

也可以看看这些库:

  • Styletron
  • Fela
  • Style-Sheet
  • cxs
  • otion
  • css-zero
  • ui-box
  • style9
  • stitches
  • catom

React-Native-Web

React-Native-Web 是一个非常有趣的库,让浏览器也可以渲染 React-Native 原语。不过我们这里并不讨论跨平台开发(演讲里有更多细节)。

作为 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 是一个新的 CSS-in-JS 库,Facebook 团队为了 2020 年的 Facebook 应用重构而开发它。未来可能会开源,有可能用另一个名字。

值得一提的是,React-Native-Web 的作者 Nicolas Gallagher 被 Facebook 招安。所以里面出现一些熟悉的概念一点也不奇怪。

我的所有信息都来自演讲 :),还需要等待更多的细节。

可扩展性

不出所料,在 Atomic CSS 的加成下,Twitter 和 Facebook 的 CSS体积都大幅减少了,现在它的增长遵循的是对数曲线。不过,简单的应用则会多了一些 初始体积

Facebook 分享了具体数字:

  • 旧的网站仅仅首页就用了 413Kb 的 CSS
  • 新的网站整个站点只用了 74Kb,包括暗黑模式

源码和输出

这两个库的 API 看起来很相似,但也很难说,因为我们对 Stylex 了解不多。

值得强调的是,React-Native-Web 会扩展 CSS 语法糖,比如 margin: 0,会被输出为 4 个方向的 margin 原子规则。

以一个组件为例,来看看旧版传统 CSS 和新版原子 CSS 输出的区别。

<Component1 classNames="class1" /> <Component2 classNames="class2" />
复制代码
.class1 {background-color: mediumseagreen;cursor: default;margin-left: 0px;
}
.class2 {background-color: thistle;cursor: default;jusify-content: flex-start;margin-left: 0px;
}
复制代码

可以看出这两个样式中 cursormargin-left 是一模一样的,但它在输出中都会占体积。

再来看看原子 CSS 的输出:

<Component1 classNames="classA classC classD" />
<Component2 classNames="classA classB classD classE" />
复制代码
class a {cursor: default;
}
class b {background-color: mediumseagreen;
}
class C {background-color: thistle;
}
class D {margin-left: 0px;
}
class E {jusify-content: flex-start;
}
复制代码

可以看出,虽然标签上的类名变多了,但是 CSS 的输出体积会随着功能的增多而减缓增长,因为出现过一次的 CSS Rule 就不会再重复出现了。

生产环境验证

我们看看 Twitter 上的标签是什么样子的:

现在,让我们来看看新 Facebook:

很多人可能会被吓到,但是其实它很好用,而且保持了 可访问性。

在 Chrome 里检查样式可能有点难,但 devtools 里就看得很清楚了:

CSS 规则顺序

与手写的工具/原子 CSS 不同,JS 库能让样式不依赖于 CSS 规则的插入顺序。

在规则冲突的情况下,生效的不是标签上 class attribute 中的最后一个类,而是样式表中最后插入的规则。

以这张图为例,我们期望的是书写在后blue 类覆盖前面的类,但实际上 CSS 会以样式表中的顺序来决定优先级,最后我们看到的是红色的文字。

在实际场景中,这些库避免在同一个元素上写入多个规则冲突的类。它们会确保标签上书写在最后的类名生效。其他的被覆盖的类名则被规律掉,甚至压根不会出现在 DOM 上。

const styles = pseudoLib.create({red: {color: "red"},blue: {color: "blue"},
});// 只会输出 blue 相关的 CSS
<div style={[styles.red, styles.blue]}>Always blue!
</div>// 只会输出 red 相关的 CSS
<div style={[styles.blue, styles.red]}>Always red!
</div>
复制代码

注意:只有使用最严格的原子 CSS 库才能实现这种可预测的行为。

如果一个类里有多个 CSS 规则,并且只有其中的一个 CSS 规则被覆盖,那么 CSS-in-JS 库没办法进行相关的过滤,这也是原子 CSS 的优势之一。

如果一个类只有一个简单的 CSS 规则,如 margin: 0,而覆盖的是 marginTop: 10。像 margin: 0 这样的简写语法被扩展为 4 个不同的原子类,这个库就能更加轻松的过滤掉不该出现在 DOM 上的类名。

仍然喜欢 Tailwind?

只要你熟悉所有的 Tailwind 命名约定,你就可以很高效的完成 UI 编写。一旦你熟悉了这个设定,就很难回到手写每个 CSS 规则的时代了,就像你写 CSS-in-JS 那样。

没什么能阻止你在原子 CSS-in-JS 的框架上建立你自己的抽象 CSS 规则,Styled-system 就能在 CSS-in-JS 库里完成一些类似的事情。它基于一些约定创造出一些原子规则,在 emotion 中使用它试试:

import styled from '@emotion/styled';
import { typography, space, color } from 'styled-system';const Box = styled('div')(typography, space, color);
复制代码

等效于:

<BoxfontSize={4}fontWeight="bold"p={3}mb={[4, 5]}color="white"bg="primary"
>Hello
</Box>
复制代码

甚至有可能在 JS 里复用一些 Tailwind 的命名约定,如果你喜欢的话。

先看些 Tailwind 的代码:

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

我们在谷歌上随便找一个方案,比如我刚刚发现 react-native-web-tailwindcss:

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

就生产力的角度而言,并没有太大的不同。甚至可以用 TS 来避免错别字。

结论

这就是我要说的关于原子 CSS-in-JS 所有内容。

我从来没有在任何大型生产部署中使用过原子 CSS、原子 CSS-in-JS 或 Tailwind。我可能在某些方面是错的,请随时纠正我。

我觉得在 React 生态系统中,原子 CSS-in-JS 是一个非常值得关注的趋势,我希望你能从这篇文章中学到一些有用的东西。

我组建了一个前端学习交流群,学习前端技术。给大家分享学习资料,给大家匹配学习伙伴,定期组织大家进行项目实战。想要加入一起学习的小伙伴可以点击链接加入群聊【web前端技术群】:web前端技术群

Facebook 重构:抛弃 Sass / Less ,迎接原子化 CSS 时代相关推荐

  1. 重新构想原子化 CSS

    感谢印记中文的 QC-L[1] 对本文进行翻译,英文原文: English Version[2]. 本文会比往期文章相对长些.这是我个人的一个重大的工具发布,有许多内容我想谈论.如果你能花些时间读完, ...

  2. 【vue3】css原子化

    一.制作原子化css 简单介绍 css原子化:Let's Define Exactly What Atomic CSS is | CSS-Tricks - CSS-Tricks. 如果将编程语言(c/ ...

  3. 梦回2008样式原子化:Tailwind是如何让Facebook拜倒在她的石榴裙下?

    文章目录 前言 一.什么是CSS原子化? 二.Tailwind的使用心得以及优缺点分析 优点 缺点 总结 明天,又是充满希望的一天! 前言 近些年前端掀起了一股"诡异风气",即CS ...

  4. mysql 5.7 ddl 原子_mysql 8 新特性二DDL操作的原子化

    MySQL 8.0支持原子数据定义语言(DDL)语句.这个特性被称为原子DDL.原子DDL语句将数据字典更新.存储引擎操作和与DDL操作相关联的二进制日志写入组合成单个原子操作.即使服务器在操作过程中 ...

  5. 基础才是重中之重~何为原子化操作

    占占定义: 原子化操作,操作原子化,这在软件开发中经常被听到,那到底什么是操作原子化呢,其实从字面上不难理解,原子化就是一体化,整体化,原子化操作就是将多个操作组合在一起,要么这个组合一起发生,要么一 ...

  6. 最大字段和_使对易失性字段的操作原子化

    最大字段和 总览 易失性字段的预期行为是,它们在多线程应用程序中的行为应与在单线程应用程序中的行为相同. 禁止它们表现相同的方式,但是不能保证它们表现相同的方式. Java 5.0+中的解决方案是使用 ...

  7. 易语言 字段重复_使对易失性字段的操作原子化

    易语言 字段重复 总览 易失性字段的预期行为是,它们在多线程应用程序中的行为应与在单线程应用程序中的行为相同. 禁止它们表现相同的方式,但是不能保证它们表现相同的方式. Java 5.0+中的解决方案 ...

  8. 使对易失性字段的操作原子化

    总览 易失字段的预期行为是,它们在多线程应用程序中的行为应与在单线程应用程序中的行为相同. 禁止它们表现相同的方式,但不能保证它们表现相同的方式. Java 5.0+中的解决方案是使用AtomicXx ...

  9. 删库跑路?不可回滚?MySQL创建和管理表,修改清空表,MySQL8新特性DDL原子化,完整详细可收藏

    文章目录 1.基础知识 2.创建和管理数据库 3.创建表 4.修改表 5.重命名表 6. 删除表 7.清空表 8.MySQL8新特性-DDL的原子化 1.基础知识 1.1 一条数据存储的过程 存储数据 ...

最新文章

  1. hp服务器ilo批量配置脚本
  2. linux系统启动盘怎么制作工具,windows系统制作linux启动盘工具介绍
  3. C++习题 商品销售(商店销售某一商品,每天公布统一的折扣(discount)。同时允许销售人员在销售时灵活掌握售价(price),在此基础上,一次购10件以上者,还可以享受9.8折优惠。)...
  4. SAP Sybase Adaptive Server Enterprise:高性能,低风险
  5. 【高效解法】1065 单身狗 (25分)_27行代码AC
  6. 64位内核开发第二讲.内核编程注意事项,以及UNICODE_STRING
  7. 转载一篇阅读文章(还算不错吧)
  8. 区块链的5大安全风险
  9. jenkins显示html样式问题的几种解决方案总结
  10. 中国无线城市市场发展策略及未来前景规划报告2022年版
  11. 基于JSP和sql server小区物业管理系统毕业论文
  12. 在聊天群中,如何快速获取信息,了解群中讨论的话题。
  13. Project Ara 手机,拼装的DIY手机
  14. 2021最新Spring Boot 面试题
  15. java List与json的转换
  16. PNP三极管和NPN三极管的开关电路
  17. iOS 布局 Masonry详解
  18. SMETA认证咨询|会员Alsico欧洲最大的工作服生产商之一不断提升供应商工厂条件
  19. 一篇文章告诉你SLC、MLC、TLC和QLC究竟有啥区别?
  20. 中兴c300业务板_中兴C300 GTGO ETGO GTGOE GTGOG OLT业务板 EPON GPON

热门文章

  1. 市北教师教育GHOST_XP_SP2 博采众长完美版
  2. 路飞学城python开发入门学习
  3. markdow语法之向上取整与向下取整公式
  4. 外部表不是预期的格式。
  5. VS项目(工程)属性配置的总结
  6. 将X映射到0-1、a-b 公式(归一化公式)
  7. vue 使用swiper详细步骤
  8. python shutil_[转]Python shutil 模块
  9. 关于亚马逊Neptune图形数据库简介
  10. Web自动化工具对比