不要再在 JavaScript 中写 CSS 了

本文作者是 react-css-modules 和 babel-plugin-react-css-modules 的作者。并不是对 CSS in JavaScript: The future of component-based styling,或是使用样式组件的反对,而是一种补充,web 开发者要了解自己的需求,明白自己使用 styled-components 的真正原因。

不要再在 JavaScript 中用 CSS了

9 个谎言

CSS 不应随意放置。许多项目选择将样式写在 JavaScript 中的理由不对。本文列出了常见的误解,以及解决问题的现存 CSS 方案。

本文的任何言论都没有对某个项目或人进行人身攻击的意思。styled-components 是 React 的目前趋势,所以我将 styled-components 定义为“JavaScript 中的 CSS”。

styled-components 的发起人(Max StoiberGlen Maddern 以及所有的贡献者)都很聪明、想法独特,出发点也是好的。

为了完全透明,我还要指出我是 react-css-modulesbabel-plugin-react-css-modules 的作者。

小红帽

CSS 和 JavaScript 历史

层叠样式表(CSS)是为描述标记语言文档的展现样式而出现的。JavaScript 是为了组合图片、插件等组件而创造的一种“胶水语言”。随着发展,JavaScript 拓展、转变,有了新的应用场景。

Ajax 的出现(2005)是一个重要的里程碑。这时 Prototype、jQuery、MooTools 等库已经吸引了大量的拥护者,共同解决后台跨浏览器数据获取问题。这又引发了新的问题:如何管理数据?

到了 2010 年,Backbone.js 出现,成为了应用状态管理的行业标准。不久后,Knockout 和 Angular 双向绑定的特点吸引了所有人。之后,React 和 Flux 出现,开启了单页应用(SPA)的新纪元,组件构造应用。

那么 CSS 呢?

借用 styled-components 文档中的话:

纯 CSS 的问题在于它产生的那个时代,网站由文档组成。1993 年,网站产生,主要用于交换科学文献,CSS 是设计文献样式的解决方案。但是如今我们构建的是丰富的、面向用户的交互应用,而 CSS 并不是为此而生的。

我不这么认为 。

CSS 已经发展到可以满足现代 UI 的需求了。过去十年中出现的新特性数不胜数(pseudo-classes、pseudo-elements、CSS variables、media queries、keyframes、combinators、columns、flex、grid、computed values 等等)。

从 UI 的角度看,“组件”是文档中一个独立的片段(<button /> 就是个组件)。CSS 被设计用来样式化文档,包括所有组件。问题在哪?

俗话说:“工欲善其事必先利其器”。

Styled-components

styled-components 可以用标记模板字面量在 JavaScript 中写 CSS。这样就省去了组件和样式间的匹配 ——组件由细粒度的样式结构组成,比如:

结果:

Live demo

styled-components 目前是 React 的 趋势 。

我们要理清一件事情:styled-components 只是 CSS 层面的高度抽象。它只是解析定义在 JavaScript 中的 CSS,然后生成对应 CSS 的 JSX 元素。

我不喜欢这个趋势,因为存在很多误解。

我在 IRC、Reddit 和 Discord 上调查了大家使用 styled-components 的原因,整理了一份选择使用 styled-components 常见原因的列表 。我称之为 myths。

Myth #1:避免全局命名空间和样式冲突

我把这条算作 myth 是因为它听起来就像之前这些问题没有得到解决一样。CSS ModulesShadow DOM 还有很多命名协议(比如 BEM)已经早就在社区中解决了这个问题。

styled-components(就像 CSS modules)只是替人完成了命名的任务。人总会犯错,计算机犯错少点而已。

但就本身而言,这并不是使用 styled-components 的好理由。

Myth 2:styled-components 可以简明代码

通常伴随着如下的例子:

首先——关系不大。差异基本可以忽略。

其次,说的也不对。字符数量取决于样式命名。

这同样适用于本文之后的构造样式(Myth 5:给组件设置条件样式更简单)。styled-components 只是在多数基本组件的情况下稍胜一筹。

Myth 3:styled-components 使人更关注语义化

前提就不对。样式和语义化代表着不同的问题,需要不用的应对方案。引用 Adam Morse(mrmrs)的话:

内容语义化和视觉样式 没有半点关系。当我用乐高建造东西时,我从来不会想“这是引擎的一部分”,我想着“这是个 1×4 的蓝色乐高,我用来随便做什么都行”。不论水下潜水基地还是飞机——我清晰地知道怎么用这个乐高块。

http://mrmrs.io/writing/2016/03/24/scalable-css/

(强烈建议读一读 Adam 关于 可拓展 CSS 的文章)

我们还可以举个例子看看两者是否相关。

示例:

语义化是要使用正确的标签构造标记。你能知道这些组件会渲染成什么 HTML 标签吗?不,你不知道。

和下面这段代码比较下:

Myth 4:拓展样式更容易

v1 版本可以用 styled(StyledComponent) 拓展样式;v2 引进了 extend 方法来拓展已存在的样式,比如:

这挺好。但是你可以在 CSS 中完成(或者使用 CSS 模块组合 或 SASS 继承混合 @extend)。

难道不比 JavaScript 简单?

Myth 5:给组件设置条件样式更简单

这点是说你可以根据组件属性给组件设置样式,比如:

这在 React 中很有用。毕竟组件行为就是由属性控制的。给属性值直接绑定样式有意义吗?可能吧。但是来看看组件的实现代码:

利用 JavaScript 按条件创造样式表是挺强大的,但是这也意味着样式难以理解,对比以下 CSS:

这样 CSS 更简短(229 VS 222 字符),(个人认为)也更容易理解。此外,还可以用预处理器使 CSS 分组、更短:

Myth 6:有利于代码组织

有些人告诉我他们喜欢 styled-components,因为它可以让样式和 JavaScript 在一个文件中。

我理解同一组件有许多文件很烦,但是把样式和标记塞进一个文件的方法很糟糕。这样不仅版本控制难以回溯,而且所有组件都需要滚动很长一段距离,而不是简单地点下按钮。

如果一定要把 CSS 和 JavaScript 放在一个文件中, 可以考虑使用 css-literal-loader。它可以在 build 时用 extract-text-webpack-plugin 提取 CSS,用标准 loader 配置处理 CSS。

Myth 7:DX 很方便,这工具太棒了!

很明显你没用过 styled-components

  • 一旦样式写错了,整个 app 会崩溃,并输出长长的调用栈错误(v2 更奇葩)。相比之下,CSS “style error” 只是元素渲染地不对而已。
  • 元素没有 className,所以调试时不得不去对比 React 元素树和 DevTools DOM 树(v2 可以用 babel-plugin-styled-components 定位)。
  • 没有语法检查(有一款 样式检查插件 正在开发中)。
  • 不合法的样式会被忽略(比如:clear: both; float left; color: #f00; 不会报 error 或 warning,只能祈祷调试好运了,即使看了 styled-components 源码,还是花了我 15 分钟查看调用栈。最后我在聊天中把代码粘出来寻求帮助,才有人提醒是少了:。你注意到了吗?)
  • 支持语法高亮、代码补全以及其它 IDE 细节的 IDE并不多。如果你在金融或政府机构工作,很可能无法使用 Atom IDE。

Myth 8:性能更好,bundle 更小

  • 事实是,styled-components 无法提取静态 CSS 文件(比如使用 https://github.com/webpack-contrib/extract-text-webpack-plugin)。这意味着浏览器无法开始解释样式直到 styled-components 解析、加载到 DOM上。
  • 缺少文件分离意味着无法分开缓存 CSS 和 JavaScript。
  • 所有样式化的组件都会额外包装一层 HoC。这是不必要的性能损耗。因为类似的结构缺陷,我终止了 https://github.com/gajus/react-css-modules(但创建了 https://github.com/gajus/babel-plugin-react-css-modules)。
  • 因为 HOC,如果在服务端渲染,会导致标记文档大很多。
  • 有 keyframes, 我也不需要用动态样式值做动画。

Myth 9:它可以开发响应式组件

这说的是依据环境给组件设置样式的能力,比如父容器偏移量、子元素数量等。

首先,styled-components 和响应式没什么关系。这已经超出了这个主题的范围。这种情况最好直接设置组件的 style,以避免额外的成本。

但是,元素查询是个有趣的问题,也逐渐成为 CSS 中的一个高热话题,主要是 EQCSS 等类似项目。元素查询和 @media queries 在语法上很相似,只是元素查询操作具体某些元素。

{selector} 是 CSS 选择器对应着一或多个元素。例如:#id 或 .class

{condition} 由尺寸和值组成。

{css} 可以包含:任何合法的 CSS 规则。(例如:#id div { color: red }

元素查询可以用 min-widthmax-widthmin-heightmax-heightmin-charactersmax-charactersmin-childrenmax-childrenmin-linesmax-linesmin-scroll-xmax-scoll-x 等 (详见 http://elementqueries.com/)条件给元素设置样式。

总有一天类似 EQCSS 的内容也会出现在 CSS 标准中的(希望如此)。

等下!

大部分内容都长期有效,无论是社区、React 变更或 styled-components 本身。但意义何在?CSS 已被广泛支持,有大量的社区,也确实行之有效。

本文的目的并不是阻止读者在 JavaScript 中使用“CSS”或是 styled-componentsstyled-components 一个很棒的使用场景是:更好的跨平台支持性。不要因为错误的理由使用它。

那么我们应该用什么呢?

使用 Shadow DOM v1 还为时尚早(51% 支持率)。CSS 应遵循命名协议(建议 BEM),如果担心类名冲突(或懒得用 BEM),可以用 CSS modules。如果你在开发 React web,考虑用 babel-plugin-react-css-modules。如果在开发 React Native,styled-components 更好。

感谢 Max Stoiber。

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

2 3 收藏 评论

关于作者:古鲁伊

立志做一名有格调的程序媛 个人主页 · 我的文章 · 34

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部