这么久以来,各种各样的框架试图让 web 组件化。到目前为止,react 基本做到了这一点:用一个自定义标签的方式组织 html 在一起。
<Wrapper>
<Header />
<ProductList />
<Footer />
</Wrapper>
上面的这种写法在传统的 web 开发中真是不敢想象,然而在 react 中的确实现了。如果你使用 create-react-app
这样的脚手架工具,你可以快速的搭建起来这样的体系。然而,即便是这样子,web 组件化依然有一个点没有解决:如何将样式和组件绑定在一起。当然,试图解决这个问题的工具有很多,也有很多人不认为这是一个问题。我在这里试图解释一些观点,并阐述为什么我觉得用 styled-components
可以在一定程度上解决一系列问题。
写 css 的最佳方式
目前,react 阵营对写 css 这个问题有两个阵营。一个阵营表示 css 应当和 js 写在一起,而另一个阵营则认为 css 原本是可以和 js 分离的。我们在这里做一个简单的例子。
首先是 css in js 的例子:
const style = {
margin: "1em 2em",
color: "gray",
background-color: "white"
};
const StyledDiv = (props) => {
return <div style={style}>A test</div>
};
而 css 和 js 分离就很简单了:
.styled-div {
margin: 1em 2em;
color: gray;
background-color: white;
}
import "./StyledDiv.css";
const StyledDiv = (props) => {
return <div className="styled-div">A test</div>
};
当然,这里展示的 css in js 只是一种非常原始的方式:用 object 直接将 style 注入到组件中。这样做的好处有两个:
- css 不在是全局的了,style 的生命周期与生命范围终于和 component 一致了,那么因为全局变量导致的可怕的为何灾难缓解了
- 在组件内对样式的操纵可以直接进行,无需通过 className 处理,当然也避免了创建全局的 className 了
可以看到,这里基本上就是以解决 css 的全局性为出发点的。
而 css 和 js 的分离当然也有其天然的优势:
- 可以用 css 的方式写 css,css 选择器随便用
- 我还可以加各种 preprocessor 和 post processor,比如写 scss 比如加 auto-prefix
对我来说,用 object 的方式去写 css 体验实在是太差了。而且作为 css
的 cascade
,如果不能用多级的选择器去定位 css 而是在一层层的 html 元素中添加样式简直就是噩梦。我不觉得这样的可维护水平比全局 css 要高...所以我觉得如果能把两者的优势结合在一起,就应该是一个可以被更多人介绍的方式:
- 用 css 的语法写 css
- 能创建局部 className
- 支持 preprocessor 和 postprocessor
那么在这里就不得不提另外一个有意思的东西:css-modules。它的主要思想是通过为 css 生成随机的类名称的方式来建立一种局部类命名的方式。
styled-components
基本上集成了这个工作,并在此基础上基本实现了以上的三点要求。
const Summary = styled.div`
margin-top: 2em;
text-align: right;
.price {
color: #ff0036;
font-size: 1.2em;
}
&> * {
display: inline-block;
margin-left: 1em;
}
`;
-
Summary
的 css 是以 css 的方式编写的,支持多层次的定义 -
styled-components
会把上面定义的 css 以一个特别的 className 的方式注入到元素上,实现了局部类定义 -
styled-components
支持了基本的类似于 scss 的嵌套语法(还支持 extend 语法,这里并没有展示),并且内嵌了autoprefix
的模块
我最近开始在一个项目上使用它,整体来说还是感觉不错。
兼容现在已有的 react components 和 css 框架
styled-components
采用的 css-module 的模式有另外一个好处就是可以很好的与其他的主题库进行兼容。因为大部分的 css 框架或者 css 主题都是以 className 的方式进行样式处理的,额外的 className 和主题的 className 并不会有太大的冲突。你可以认为这是一个应当使用全局 css 的地方(所以我并不赞成用 styled-components 里面的 theming 接口去做这件事)。相对于以 object 的方式写 style 的 material-ui 真是好太多了,看看 material-ui 讲述如何进行样式自定义就知道这并不是一个很成熟的想:
- css 内嵌到组建里影响了组件自身结构的表现
- inline style 意味着最高的优先级,其无法和其他的主题库配合
styled-components
的语法同样支持对一个 React 组件进行扩展:
const StyledDiv = styled(Row)`
position: relative;
height: 100%;
.image img {
width: 100%;
}
.content {
min-height: 30em;
overflow: auto;
}
.content h2 {
font-size: 1.8em;
color: black;
margin-bottom: 1em;
}
`;
这里我把 ant design 做为我默认的样式库,在其基础上我对其一些元素做了增强。两者可以很好的在一起使用。
这里符一个 github 项目 里面包含了很多使用 styled-components
的例子。