什么是原子化 CSS
首先,我们为原子化 CSS 给出一个定义:
原子化 CSS 是一种 CSS 架构方式,倾向于使用小巧且用途单一的 class,并以视觉效果进行命名。
本质上,你可以将其理解为一种工具类 CSS。例如:
.m-0 {
margin: 0;
}
.text-red {
color: red;
}
使用示例:
<div class="w-100px h-100px bg-red text-#fff p-4px">
atom css
</div>
一些比较流行的原子化 CSS 框架包括 TailwindCSS 和 UnoCSS。目前,TailwindCSS 已经成为事实上的原子化 CSS 标准。
CSS 使用方案的进化史
在详细介绍原子化 CSS 之前,我们先来回顾一下 CSS 使用方案的进化史:
1. 内联样式 (Inline Styles)
最原始的 CSS 使用方式。
优点:
- 简单直接,容易理解。
- 适用于快速试验和简单的样式需求。
缺点:
- 无法复用样式,导致代码冗余。
- 样式权重高,不利于动态修改。
- 难以维护和管理,尤其是在大项目中。
<div
style={{
backgroundColor: "red",
color: "#fff",
padding: "10px 20px",
borderRadius: "10px",
fontSize: "20px",
cursor: "pointer"
}}
>
button
</div>
2. 传统 Class + BEM 命名
BEM(Block, Element, Modifier)是一种命名约定,旨在解决 CSS 的命名冲突和复用问题。
优点:
- 结构清晰,模块化强。
- 提高样式的复用性。
- 减少命名冲突,增强代码的可维护性。
缺点:
- 命名较为复杂,学习成本较高。
- 文件和代码量较大,可能显得冗长。
- 重名class可能会导致样式覆盖问题(缺少模块化)。
<div class="button button--primary button__icon"></div>
3. CSS Modules
CSS Modules 是一种模块化的 CSS 方案,默认将样式局部化,避免全局命名冲突。也是目前比较流行的css解决方案。
优点:
- 样式局部化,避免全局污染。
- 可以使用 CSS 原生语法,易于上手。
- 支持 CSS 预处理器。
缺点:
- 需要构建工具的支持。
- class样式书写较为繁琐。
- 生产环境下的 class 名称会增加随机字符,不利于调试。同时常规的 class 选择器无法使用。
/* styles.module.css */
.button {
background-color: blue;
}
import styles from './styles.module.css';
function Button() {
return <button className={styles.button}>Button</button>;
}
4. CSS-in-JS
CSS-in-JS 是一种基于 JavaScript 和 CSS 的样式解决方案,利用标签模板字面量创建组件级别的样式。曾经是 React 社区中最流行的 CSS-in-JS 解决方案之一,但是现在已经逐渐被社区抛弃。
优点:
- 样式与组件高度耦合,提升可维护性。
- 动态样式支持强,可以使用 JavaScript 变量和逻辑。
- 自动处理前缀和作用域,简化样式编写。
缺点:
- 性能太差,会导致页面加载速度变慢。
- 样式在组件内定义,可能导致文件过大。
- 动态生成的 class 名称不利于调试。
- 有一定的学习成本。
import styled from 'styled-components';
const Button = styled.button`
background-color: blue;
color: white;
padding: 10px 20px;
border-radius: 10px;
font-size: 20px;
cursor: pointer;
`;
function App() {
return <Button>Button</Button>;
}
5. Scoped CSS
Scoped CSS 是一种将 CSS 样式作用范围限定在特定组件或模块内的技术,常见于 Vue.js 中。
优点:
- 样式局部化,避免全局污染。
- 适用于单文件组件,结构清晰。
- 与传统 class 语法书写一致,易于上手。
缺点:
- 需要框架支持,目前仅流行于 Vue、Svelte等少数框架。
- 样式共享和复用较为复杂。
<template>
<div class="button">Button</div>
</template>
<style scoped>
.button {
background-color: blue;
color: white;
padding: 10px 20px;
border-radius: 10px;
font-size: 20px;
cursor: pointer;
}
</style>
原子化css
原子化css是一种css的编写方式,它将css样式拆分成最小的单元,然后通过组合这些单元来构建样式。这种方式的好处是可以提高样式的复用性,减少代码冗余,提高开发效率。
写一个原子化css的例子
优点
原子化css最大的优点就是提效。
传统的样式开发流程一般是:新建样式文件 → 起一个class名称 → 编写css代码 → 导入样式文件 → 在div上填写class名称。
原子化css的流程是这样:在div上填写class名称。
在维护方面,虽然原子化css也简化了传统css的查找流程,但是也带来了一些新的问题,那就是当class过多时,你想要通过控制台或者代码快速定位到目标class名,也不是一件容易的事,所以我认为在维护方面不算是有大的提升,甚至是带来了一些小问题。
> 解决了css私有化的问题
由于原子化css本来就是全局样式,所以根本上解决了css私有化的问题,因为不再需要私有了。这样就不用担心样式覆盖的问题,也不用担心样式冲突的问题。
> 降低命名负担,减少了嵌套命名
在传统的css编写方式中,我们需要为每个样式起一个名字,这样就会导致样式的命名负担,还要时刻担心重名class覆盖问题。而原子化css就不需要为每个样式起一个名字,从而降低了命名负担。同时减少了嵌套命名,例如BEM命名,提高了开发效率。
> 样式复用性高,减少代码冗余
原子化css虽然增加了class名的数量,但是通过组合这些样式,可以实现样式的复用,减少代码冗余。因为在这种模式下,你能预见到css文件的最大总量是固定的,同时框架本身还有 tree-shaking 的功能,可以去除没有使用的样式。要知道,你的网页加载越快,留存的用户就会越多。
> 与其他样式方案不冲突,可以结合使用
原子化css优秀的一点就是,他跟其他样式方案不是互斥的,这意味着你可以无缝的将原子化css结合到你的项目当中。
> 支持pxtorem、pxtovw等插件,完美兼容移动端
目前原子化框架和postcss的兼容性较好,可以通过postcss将px转换成rem等。完美适合移动端项目使用
> 有利于团队协作
原子化css的样式是固定的,不会因为不同的开发人员而有所不同,这样就可以减少团队协作中的样式冲突问题。 同时,当我们接手多个项目时,可以最大限度的降低样式方面维护难度。
> 开发时不用在html和css之间来回切换
同样提高了开发效率。而且使用原子化css根本就不需要写css,也就不存在css文件了,只需要在html中添加class名即可。
利用这个特性,我们还可以做一些插件:例如蓝湖插件,一键生成 原子化class 名,方便开发人员使用。
缺点
> 有一定的上手成本
因为目前流行的原子化css框架,提供的class名虽然是大部分基于css属性的,但是也有一些不是,这样就需要开发人员去记忆这些class名,这样就增加了一定的上手成本。
> 复杂样式类名太多难以阅读和维护
原子化css虽然提高了样式的复用性,但是也增加了class名的数量,这样就导致了class名太多,难以阅读和维护。例如:
<button
class="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600 text-sm text-white font-mono font-light py-2 px-4 border-2 rounded border-blue-200"
>
Button
</button>
解决方案有一些,但是目前都不够完美。
<!-- Attr模式 tailwindcss目前不支持,而且复杂的样式还是会冗长难读 -->
<button
bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
text="sm white"
font="mono light"
p="y-2 x-4"
border="2 rounded blue-200"
>
Button
</button>
<!-- @apply方式,将多个原子类都继承到一个class下。但是这样又还不如直接写样式来的痛快 -->
<template>
<button class="btn-primary"></button>
</template>
<style>
.btn-primary {
@apply blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600 text-sm text-white font-mono font-light py-2 px-4 border-2 rounded border-blue-200;
}
</style>
个人使用感受
在我个人使用的过程中,也有一些小的吐槽点,之所以跟上面的缺点分开,是因为我觉得可能在别人那里这并不算是缺点。
存在一些非语义化的class
在目前主流的原子化css框架上都存在一些非语义化的class,比如m-1、p-10等。很明显,m和p我知道是margin和padding,但是后面的数字的含义可能会让我感到疑惑。
在tailwindcss中,m-1的意思是margin: 0.25rem;。但是m-0的意思我们能猜到,是margin:0。在浏览器的规则里,等价于margin:0px。单位是px,所以我在一开始用的时候下意识以为m-1是margin:1px。其次,rem的单位我们都知道,它并不是一个固定单位,它受到根元素的font-size的影响,因此这种语法我是很排斥使用的。
写法过于灵活
前面我们提到了,除了常规的class写法以外,还存在属性(attr)写法、继承(apply)写法等,假如一个团队里出现了多种写法,那这不仅没有提高协同效率,反而还带来了维护成本。就像jsx一样,带来了灵活性的同时,也让接手别人的react项目的开发人员抓狂。
可以自定义原子类
没错,原子化框架是允许你自己编写自己的原子类的。其实作为框架来说我觉得这没问题,这给开发人员带来了极大的自由度。但是,在开发时我们有多喜欢自由,那在维护时你就会有多憎恨自由。自由度是一把双刃剑。而我认为原子类应该是为了降低开发和维护难度而存在的,因此不应该过度自由。
如何正确的使用原子类css
在我日常的使用过程中,其实我发现使用原子类的基础功能已经可以满足日常90%的场景了。剩下的10%可以结合以前的class方案来去解决。至于前面提到的一些非标准化的原子类写法,从维护的角度还是尽量不要使用了。