共计 7025 个字符,预计需要花费 18 分钟才能阅读完成。
文章由 ssh 翻译 / 润色,原文地址。
首发公众号「前端从进阶到入院」,欢送关注,加我好友探讨。
随着 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 这个库的理念十分靠近。
<button
class="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 的问题:
- 全局命名空间
- 依赖
- 无用代码打消
- 代码压缩
- 共享常量
- 非确定性(Non-Deterministic)解析
- 隔离
实用工具 / 原子 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;
}
能够看出这两个款式中 cursor
和 margin-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);
等效于:
<Box
fontSize={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 是一个十分值得关注的趋势,我心愿你能从这篇文章中学到一些有用的货色。
感激浏览。
感激
本文首发于公众号「前端从进阶到入院」,欢送关注。
字节跳动招人啦,明年光是咱们组就新增了十几个 HC,真的很缺人。北上广深杭都有,随时面试都能够,我会帮你做好全方位的内推服务!欢送通过留言、公众号分割到我征询 ????。