138

精读《css-in-js 杀鸡用牛刀》 - 知乎专栏

 6 years ago
source link: https://zhuanlan.zhihu.com/p/30118092?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

精读《css-in-js 杀鸡用牛刀》

前端开发话题下的优秀答主

本期精读的文章是:css-in-js 杀鸡用牛刀

精读《请停止 css-in-js 的行为》 这篇文章之后,我们又读了一篇抵制 css-in-js 的文章,虽然大部分观点都有道理,但部分存在可商榷之处,让我们分析一下这篇文章,了解 css 还做了哪些努力,以及 css-in-js 会如何发展。

2 内容概要

2.1 结构/行为 vs 样式

作者认为,模块化 jsx 让 html 结构与行为耦合在一起是很有价值的,然而样式却不应该与模块耦合起来,因为样式是一种全局行为。许多时候需要对网站进行全局的设计,将样式分散到模块中会导致更多的理解成本。

2.2 松耦合与紧耦合

将样式与模块松耦合,系统会获得更大的自由度与拓展性。如果样式与结构松耦合,一套看似相似的的元素,可能拥有完全不同的底层结构。然而交互必须与结构紧耦合,因为交互依赖于结构。

2.3 视觉一致性问题

局部样式会阻碍视觉一致性,只有全局化样式才能保证视觉一致性。

2.4 代码复用问题

如果每个组件维护自己的样式,那么会存在许多样式代码复制粘贴的问题,复制粘贴的代码可维护性极低。

无论是 css-in-js 还是 css 预编译的尝试,各自都具有强大优点,本文对 css-in-js 提出的质疑我认为是欠妥当的,下面谈谈 css-in-js 如何解决作者提出的问题,以及简单介绍 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 的思路。

3.1 css-in-js 依然具备视觉一致性

文中提出,网站样式要从全局考虑,模块化样式行为的优点是解决了样式冲突问题,但因此也削弱了对全局样式的把控。

开发单个组件的样式分为两种情况,分别是明确风格的组件与样式独立的组件,在样式独立组件中,由于不确定会被哪些主题的网站所引用,因此无论是全局 css 还是局部 css,都无法控制样式。在明确风格的情况下,可以先把此风格的基色确定下来,无论是抽成 sass 变量还是 js 变量,都具有可复用性。

全局 css 的开发,适合自上而下控制,组件通过定义 class 而不需要关心具体样式,通过全局 class 统一调控整体风格。而 css-in-js 是自下而上的,但需要预先抽出整体风格的样式模块,其效果与全局 css 是等价的。

全局 css 控制风格:

<style>
	.container{}
	.list-item{}
	.submit-button{}
</style>

<div className="container">
	<div className="list-item"></div>
	<div className="list-item"></div>
	<div className="submit-button"></div>
</div>

css-in-js 风格:

const CommonContainer = styled.div``
const CommonListItem = styled.div``
const CommonSubmitButton = css``

export const Container = styled(CommonContainer)``
export const ListItem = styled(CommonListItem)``
export const CommonSubmitButton = styled.div`
	${CommonSubmitButton}
`

而 css-in-js 运行时的样式解析,让我们更轻易的切换主题。比如我们抽出一个公共样式包,业务代码中的色值都从此样式包中引用,那么在不同的环境下,公共样式包可能通过所在宿主环境的判断,返回给业务代码不同的色值,甚至与宿主环境配合,从宿主环境拿到注入的颜色,实现一套代码在运行时轻松换肤。

3.2 css-in-js 仍具备代码复用性

文中观点提出,css-in-js 这种局部样式行为,会导致公共样式、方法难以复用,导致各个模块参杂着大量重复代码。因为 sass 通过定义全局变量、mixins 方法让样式更具有复用性。

我觉得这是一种误解,在 css-in-js 模式中,通过全局合理的设计,使用 js 文件存放颜色变量、公共方法、可能会复用的 css 代码块,其复用能力远大于 sass。

3.3 OOCSS

OOCSS 成为 css 的面向对象加强版,每个 class 只处理一件事:

.size {width: 25%;}
.bgBlue {background:blue}
.g-bd2{margin:0 0 10px;}

网易 NEC 就大量使用了这种思想。

这样的好处在于避免了 class 之间的冗余,让我们更容易创建可复用的 class,也不会在命名上纠结。

然而,先不说 oocss 带来的巨大零散 class 导致的维护成本,以及修改 class 导致的巨大风险,class 的本意是语义化,如果让 class 使用一堆对象描述堆砌,我们将很难定位一个元素,也很难描述这个元素的含义。

3.4 SMACSS

为css分类

SMACSS 认为 css 有 5 个类别:

  1. Base 基础样式
  2. Layout 布局样式
  3. Module 模块样式
  4. State 状态样式
  5. Theme 主题样式

我们通过这 5 种类别来拼凑出完整的 class,我感觉就是对 OOCSS 的进一步规范和约束。

对这 5 种类别,在命名时要加上对应前缀,分别是:

  1. Base 属于基础元素,比如 div p,不需要命名
  2. Layout 使用 .l- 或 .layout-前缀
  3. Module 使用模块名命名,比如文章区块就叫 .article
  4. State 使用 .is- 前缀,比如 .is-show
  5. Theme 使用 .theme- 前缀

我觉得这样在语义化的基础上,拆分了状态、主题、布局,着实增强了 css 可读性。

最小化适配深度

尽可能减少适配层级,虽然增加适配层级会减少冲突发生率,但是会增加额外的阅读负担,以及一些 bug(旧版 ie 层级超过 255 导致样式失效)。

像 css-modules 这种解决方案恰恰反其道而行之,通过层级避免冲突,通过预编译解决阅读负担,然而在没有预编译的情况下,最小化适配深度原则依然是最有效的。

3.5 BEM

BEM 规范更像是 SMACSS 分类的加强版,通过 __element 表述后代,--modifier 表述状态,比如:

.article {}
.article__label {} /* label 元素 */
.article__label--selected {} /* label 元素处于被选中状态 */

3.6 ITCSS

类似 SMACSS 对 css 元素进行了分层:

  • Settings – 与预处理器一起使用,包含颜色、字体等定义
  • Tools – 工具与方法,比如 mixins,Settings 与 Tools 都不会产生任何 css 代码,仅仅是辅助函数与变量
  • Generic – 通用层,比如 reset html、body 的样式
  • Elements – 对通用元素的样式重置,比如  a p div 等元素的样式重置
  • Objects – 类似 OOCSS 中的对象,描述一些常用的基础状态
  • Components – 对组件样式的定义,一个 UI 元素基本由 Objects 与 Components 组成
  • Utilities – 工具类,比如 .hidden

ITCSS 的分层是非常有借鉴意义的,即便在 css-in-js 设计中,也可以参考此模式定义结构。

3.7 ECSS

ECSS 的规范是这样的:.nsp-Component_ChildNode-variant

  • nsp 一个尽量简短的命名空间
  • Component 文件名
  • ChildNode 子元素名
  • variant 额外内容
<div class="tl-MediaObject">
   <a href="#" class="tl-MediaObject_Link">
      <img class="tl-MediaObject_Media" src="mini.jpg" alt="User">
   </a>
   <div class="tl-MediaObject_Attribution">@BF 14 minutes ago</div>
</div>

更多细节可以看此 PPT

虽然我认为这篇文章提出的 css-in-js 缺点大部分存在漏洞,但它警示了我们,css 设计的初衷是全局化控制样式,即便产生了样式冲突、混乱的问题,但我们仍要记住,在模块化开发的今天,仍要保持网站风格的整体性,即便使用了 css-in-js 的开发方式。

虽然作者呼吁我们不要只顾着 css-in-js,要放眼看看 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 等基于原生 css 的解决方案,但我觉得把这些思想运用到 css-in-js 是个不错的选择 :p

讨论地址是:精读《css-in-js 杀鸡用牛刀》 · Issue #38 · dt-fe/weekly
如果你想参与讨论,请点击这里,每周都有新的主题,每周五发布。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK