本期精读的文章是：[css-in-js 杀鸡用牛刀](https://codeburst.io/css-in-js-is-like-replacing-a-broken-screwdriver-with-your-favorite-hammer-c9765c9ee43b)

# 1 引言

<img src="https://img.alicdn.com/imgextra/i3/O1CN01cpxa4o1zwpbWN5xZz_!!6000000006779-2-tps-356-220.png" width="500" alt="logo" />

继 [精读《请停止 css-in-js 的行为》](https://zhuanlan.zhihu.com/p/26878157) 这篇文章之后，我们又读了一篇抵制 css-in-js 的文章，虽然大部分观点都有道理，但部分存在可商榷之处，让我们分析一下这篇文章，了解 css 还做了哪些努力，以及 css-in-js 会如何发展。

# 2 内容概要

## 2.1 结构/行为 vs 样式

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

## 2.2 松耦合与紧耦合

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

## 2.3 视觉一致性问题

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

## 2.4 代码复用问题

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


# 3 精读

无论是 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 控制风格：

```html
<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 风格：

```javascript
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 只处理一件事：

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

网易 [NEC](http://nec.netease.com/library/101022) 就大量使用了这种思想。

这样的好处在于避免了 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` 表述状态，比如：

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

## 3.6 ITCSS

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

![ITCSS](https://www.xfivecdn.com/xfive/wp-content/uploads/2016/02/01083650/itcss-layers2.svg)

- 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 额外内容

例子：

```html
<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](http://ecss.io/slides2)

# 4 总结

虽然我认为这篇文章提出的 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](https://github.com/dt-fe/weekly/issues/38)

> 如果你想参与讨论，请[点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，每周五发布。
