CSS-in-JS的工具库让我们可以以一种新的方式来写css,支持动态函数、css作用域并且可移植。但是对于开发者来说,这会使得前端开发的复杂性更高,是否值得使用是具有争议的。这篇本章主要是为了让你从更高的层面去了解CSS-in-JS工具库,从原理上来看它们解决了什么问题,以及在项目中该不该使用它们。
什么是CSS-in-JS
前端主流框架React、Vue、Angular都是基于组件开发的,因此组件化开发也是主流的前端开发方式。一个组件通常是像按钮button、弹窗pop-up、导航栏这样的一个UI元素,只需要实现一次即可在应用的各个场景中使用,我们的整个单页面应用(SPA)可以由一个个组件来组成。然而,如何很好的组织SPA应用中各个组件的样式是一个难题。如果使用全局CSS文件,CSS文件中的样式会相互覆盖,导致写的样式可能不生效,使得最后展示的页面与预期的不符。对于有样式SPA应用,依赖难以管理,使得模块化的网页应用的复杂性不可预测。使用CSS-in-JS,可以让每个打包后的组件包含它所有的样式和依赖,在不依赖任何外部css文件的情况下正常工作。styled-components作者之一Max Soiber在他的博客中有写道CSS-in-JS的工作原理:
For three years, I have styled my web apps without any .css files. Instead, I have written all the CSS in JavaScript. ... I can add, change and delete CSS without any unexpected consequences. My changes to the styling of a component will not affect anything else. If I delete a component, I delete its CSS too. No more append-only stylesheets!
翻译过来:
我已经三年没有在我的网页应用中写一个css文件,所有的css都写在js代码中… 我可以毫无顾虑的添加、改变和删除css样式,对于某个组件样式的修改不会产生任何副作用。删除一个组件的同时也删除了它的CSS,不再需要使用追加属性的方式写样式。
由此看来,CSS-in-JS的核心其实就是在把CSS写在JS中,这样js模块化的同时css也实现了模块化。
常见的CSS-in-JS库
虽然说各种CSS-in-JS库的目的都是为了使得打包的组件中包含自己的样式,但是它们的特性和语法上还是有些许不同的。
基于框架
有些库只有在指定的框架下才能使用,比如radium只能用于React应用;styled-jsx只能用于jsx组件。基于框架CSS-in-JS库使用和框架相同的语法,比如styled-jsx在jsx中使用模版字符串的方式给组件添加css样式。下面是使用styled-jsx(github文档地址)创建的常规尺寸和大尺寸两种按钮:
const
Button
=
(
props
)
=>
(
<
button className
={
'large'
in props
&&
'large'
}>
{
props
.
children
}
<
style jsx
>{`
button
{
padding
:
20px
;
background
:
#
eee
;
color
:
#
999
}
.
large
{
padding
:
50px
}
`}</
style
>
</
button
>
)
/* Creates a regular button */
<
Button
>
Hi
</
Button
>
/* Creates a large button */
<
Button
large
>
Big
</
Button
>
与框架无关
其他如JSS、Emotion(https://github.com/emotion-js/emotion)、以及前面提到的Styled Conponents都是与框架无关的,可以在任何基于组件开发的框架或者原生js使用。可以将下面代码复制到JSS在线演示环境中实际体验一下CSS-in-JS:
/* Registers a Web Component with red background */
import
jss from
"jss"
;
import
preset from
"jss-preset-default"
;
jss
.
setup
(
preset
());
const
styles
=
{
wrapper
:
{
padding
:
40
,
background
:
"#f7df1e"
,
textAlign
:
"center"
},
title
:
{
font
:
{
size
:
40
,
weight
:
900
},
color
:
"#24292e"
},
link
:
{
color
:
"#24292e"
,
"&:hover"
:
{
opacity
:
0.5
}
}
};
const
{
classes
}
=
jss
.
createStyleSheet
(
styles
).
attach
();
document
.
body
.
innerHTML
=
`
<
div
class
=
"${classes.wrapper}"
>
<
h1
class
=
"${classes.title}"
>
Hello
JSS
!</
h1
>
<
a
class
=
$
{
classes
.
link
}
href
=
"http://cssinjs.org/"
traget
=
"_blank"
>
See
docs
</
a
>
</
div
>
`;
唯一选择器vs.行内样式
为了处理css作用域的问题,大部分的CSS-in-JSS的库包括JSS, Emotion和Styled Components都会自动为每个组件生成一个唯一的选择器。这个可以自己到Styled Component的主页上找几个例子用开发者工具看一下最终的效果。例如,下图是用开发者工具查看“I’m styled button”元素的结果,生成了两个唯一的选择器,一个button的 components__SecondButton-sc-1gmolv7-3kBdwIx,一个是父元素div的 components__AlignCenter-sc-1gmolv7-0iWrNby。
虽然说使用唯一的选择器是解决css作用域问题的一个比较好的方式,但是生成的代码看起来却没那么美观;有部分CSS-in-JS库采用了另一种方式来解决:直接在html中插入行内样式,Radium使用的就是这种方式。后者不仅使得html代码可读性更好,同时也具备更好的性能、源码的顺序独立以及清除无用代码,Radium文档也指出了它的缺点
尽管如此,在行内样式中有一些css特性和技术难以顾及:媒体查询、浏览状态(:hover, :focus, :active)、修饰符(不再有.btn-primay)。不过Radium提供了标准的接口和抽象层来解决这类问题。所以,选择哪种方式,哪种CSS-in-JS库,需要根据需求权衡,选择合适自己的。
独有的特性
CSS-in-JS库之间不仅语法、框架支持和作用域处理上有区别,它们各自也有自己不同于其他库的额外支持,比如:
全局选择器
基于状态的样式
客户端vs.服务端渲染(或者都支持)
缓存
source maps
内置自动前置补全(auto-prefixing)
媒体查询(media-query)
插件和工具包
其他… 大多数相关的库都有很完善的文档,Michele Bertoli整体出来的最主要的一些CSS-in-JS库的对比表格.
CSS-in-JS的优势
CSS作用域
CSS默认情况下是不支持作用域的,每个css规则都是全局生效作用域整个项目的。也正因为这样,css样式之间可能会以意想不到的方式互相覆盖。因而诞生了一系列的CSS模块化的方式:BEM、OOCSS、SMACSS等;css预处理和后处理也是一种方式。CSS-in-JS通过自动化的方式使得模块化更进一步,达到高水平的可预测性。
封装性
封装后的组件不需要在关心里面的细节逻辑,只需要关注对外提供的API来与其他组件进行交互。封装后便于维护和便于处理错误,只需要修改组件的代码,所有相关模块都能更新而且不用担心改动会使得应用中的其他模块发生未知的变化。
可移植
组件中包含了运行所需要的所有的源码、样式和逻辑,因此可以随意的移植。它们独立运行,组件之间的交互通过API来实现,可以很好的实现应用逻辑的解耦合。
复用性
组件只需要写一次,便可以在任何其他地方复用,不仅仅是在同一个应用内复用,还可以在其他相同框架的应用中复用。
动态特性
由于CSS-in-JS的代码本身就是javascript代码,在写css规则的时候可以添加循环、条件判断、变量、状态依赖等复杂的逻辑。因此在实现包含动态特性的复杂UI时,是一个很好的选择。
CSS-in-JS的缺点
学习曲线
不用说,CSS-in-JS是有学习成本的,尤其是对于那些没有基于组件编程方式经验的人来说。除此之外,需要学习新的语法,需要用新的思维方式去开发,在刚开始的时候可能会使得开发进度变慢。
额外的复杂性
使用CSS-in-JS可能使得个人的前端技术栈增加了额外且非必要复杂性。尽管CSS-in-JS很好的控制它的复杂性,但是并不一定值得去这么做,尤其是在简单的项目中只会增加麻烦。
代码可读性
自动生成的选择器使得代码难以阅读,对于经常使用开发者工具来debug的同学来说影响还是挺大的;同时对于新手来说,理解他们自己写的代码是很重要的。
所以,什么时候使用CSS-in-JS?
CSS-in-JS提供了一种直观且安全的方式来为基于组件开发的应用来实现样式。除了js框架之外,还能用于Web Component。在大型单个应用中实现复杂的交互(如:特性是基于状态改变的)使用CSS-in-JS会方便很多。对于新手来说,更多的可能是搭建静态CSS的前端页面或者是注重代码的简洁和可读性,CSS-in-JS可能并不适合。更合理的选择应该是sass、less、postCss这一类的工具,而不是为了新奇使用一个新的技术。
本文编辑:标梵互动(https://weibo.com/a/hot/7597245303101441_1.html)