踩了一圈 CSS 构建方案的坑
css 的写法一直算比较混乱的。层叠的样式表与 DOM 结构的分离看似清晰,但也因此容易产生屎山,组合太自由,哪些选择器用了哪些选择器没用,共用的嵌套的,分离的。今天小编就带你一探究竟(……)
CSS类复用粒度
我自己把 css 选择器(类)的复用粒度分三个层级。
组件类
粒度最大的层级,通常按组件级别语义化。选择器一般是下面这些名字
.wrapper
.container
.list-item
组件化的选择器下面通常有很多条的 css。
功能类
通常是共用的样式或状态,比如
.open
.close
.light
.dark
.glass-effect
这个看起来好像和组件类不冲突,但硬说的话组件类其实应该是这样
.container.open { 此处将 .open 的所有样式全覆盖 }
.container.close { 此处将 .close 的所有样式全覆盖 }
.container.light { 此处将 .light 的所有样式全覆盖 }
.container.dark { 此处将 .dark 的所有样式全覆盖 }
组件类的状态严格在组件的 scope 下。功能类则是可以不限 Scope 的复用。
这 CSS 容易混乱的根源。在工程维护角度,功能类是最不敢乱动的类,不知道动了后哪里样式就会出问题。但在设计角度,用功能类复用一些状态又确实很方便,统一设计也好用。比如增加统一的圆角、描边、阴影样式。
功能类的优缺点是一体两面——图像的只有主观的好看与否,没有客观的对错。
原子类
定义海量常用的基础样式类,在 class 上直接写类名就能获得对应效果。就是 tailwind css。
原子类相较于功能类粒度更小,也不会轻易改动 css 属性。
.flex
.col-1
.text-sm
方案
通常来说,一个库的样式会着重在一个某一个粒度上。
原生 css
用原生 css 时通常会以 组件化 的粒度为主,带极少的功能类。现在配合 css 变量使用。早期的网页简单,一个 CSS 文件就能搞定全站,设计上并没有考虑项目变得越来越复杂后的实践。
优点:性能好,扁平的结构利好小项目。适合写研究新样式。
缺点:过于扁平,大量工程化后易屎山,存在样式与 DOM 分离带来的维护混乱。
SCSS
古法预处理器,可能多层嵌套 css,可组合。是 组件化 的粒度。在 CSS.module 出来前,用 SCSS 分割 Scope 挺好用。
优点:结构非常清晰
缺点:
- 编译后的选择器很长一串,从浏览器渲染角度,匹配DOM是耗性能的
- 难以应对复杂项目 DOM 结构的改变,需要考虑扁平化 + 命名,但这样做和原生 CSS 的维护体验也不相上下。
CSS Module
CSS Module 是完全 组件化 的粒度。相比起 SCSS 的样式与 DOM 分离,CSS Module 为组件内部样式耦合,组件间样式分离。
优点: 在组件粒度分割合理的情况下,清晰易维护。
缺点:依赖预构建,写类名写起来太磨叽了。整体我用得不多没法评价。
BootStrap
组件化 为主,少量原子化修饰的预置样式库,拿来即用是不错的。早期 CSS 框架大多是指预制样式,和预构建的库有本质区别。
Tailwind css
完全原子化的神奇之库,通过编译可以有功能类和组件类。它更像是重新定义了 css 语法。
优点
- 灵活,快,好看
- 工具链齐全,可以裁剪掉不用的原子类。
缺点
- 稍微要写复杂一点的样式,DOM 就会被一大堆 class 埋没。
- 从浏览器渲染角度,匹配、合并大量 CSS 样式是需要更多性能开销的
- 要做到同种样式的复用,必须组合原子类,变成功能类或组件类,否则维护起来相当麻烦。这似乎违背了用 tailwind css 的初衷,熟悉了 css 的不如直接自己用 css 手撮功能类和组件类。
- 其实我是 tailwind 黑,嗯。但无法否认开发时确实很快很方便。
原生 css in js
指 JS Object 转译为 CSS。由于写起来太不像 CSS,复杂的功能写起来过于不直观 ,我直接 PASS。
Styled-components
组件化的 CSS in JS 方案,写起来像 CSS 实际是 JS。支持客户端动态修改 CSS 具体属性(其他方案做状态改变主要依靠 selector 的匹配)
优点:
灵活好拓展,比如主题管理不仅仅是颜色,还可以是图片资源一类的。
缺点:
- 因为是 JS 转 CSS,服务器编译慢和客户端渲染慢得选一个
- React 的 useContext 要被废弃了,而 styled-components 严重依赖此 hook,导致进入了维护状态。JS 框架发展太快了。
Linaria
自定义 功能类 的 CSS in js 方案,同时也支持 组件化 写法
优点:
是预构建方案,和原始的 CSS 写法和思路差不多。
缺点:
- 组件化鸡肋, 功能类和组件化过于割裂,反而使得组件化样式管理困难(比如组件样式和自定义原子样式写两个地方,加上DOM 上写 classname,等于写个样式得看三个窗口)
- 使用功能类有点像原子化,又完全不如 tailwind 已经给你预设好一堆东西的效率。写类名和 cssmodule 一样,太磨叽了
总之有点四不像。
构建组建库
每一个 CSS 方案都有对应的构建组件库的实践。 shadcn 是基于 tailwind 构建组件库实践。
CSS 框架选择要素
- 样式复用
- 样式组合
- 动态样式
- 主题切换
- 代码提示
- 自动裁剪
- 随意重构
- 渲染性能
- 实践的统一性
最重要的还是自己的需求。