还能在JavaScript中写CSS?

前言

好好的HTML、JavaScript、CSS为什么要写在一起,分开来写不是更好吗,这样也避免一大串合在一起影响美观呀,但是自从学习了React框架后,发现HTML和JavaScript写在一起,但是CSS写在另外一个文件,刚刚接触React时才疏学浅,认为这应该是祖祖辈辈传下来的吧,但是经过一段时间,发现这并不好维护,每个js组件带一个CSS文件,不仅在HTML中要设置class属性,还必须要用在每个css样式里面带上组件的前缀生怕污染全局css,内心不爽却也无能为力。

直到我遇见了,styled-components,仿佛像是沙漠旅行者遇见了绿洲~

为什么要CSS in JS

这点就好比一个袋子标签写着‘钥匙’里面装了所有与钥匙相关的东西,而不是一个大袋子装钥匙,还附带一个小袋子装钥匙扣钥匙环。这样相比就很明白了,我们可以理解CSS为钥匙扣和钥匙环,在传统的样式脚本分离的写法中,需要手动将钥匙和钥匙环扣在一起才能使用。但是CSS in JS就好比将钥匙环已经绑定在钥匙上,拿起即用一样。

安装

1
npm install styled-components

styled-components只需要安装一次,便可以像一个组件一样在任何地方使用,并不需要配置webpack,这一点充分体现出开箱即用的优点,我们先从一个简单的demo来展示他最基础的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from "react";
import styled from "styled-components";

class App extends React.Component {
render() {
const Wrapper = styled.div`
margin: 10px auto;
width: 300px;
height: 100px;
border: 1px solid pink;
text-align: center;
`;
const Button = styled.button`
width: 100px;
background: yellow;
`;
return (
<Wrapper>
<Button>Hello World</Button>
</Wrapper>
);
}
}

export default App;

相信你看完这段代码后,会有种 真 · 组件式开发 的感觉,直接将css写在自己定义的一个组件中,在render里面直接调用,不必写一些className乱七八糟的属性。但是,这只是styled-components的最大的特点,关键是,写CSS最重要的还是完全复原设计图,styled-components得要又其他css类库一样简便的用法。

组件样式继承

通常在原生css中一般会通过在class中传入多个name通过空格分隔来复用css样式,这样也起到了继承的作用,但是在styled-components中继承是一件非常优雅的事情,就像写class继承那么优雅:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends React.Component {
render() {
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
`;

const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;
return (
<div>
<Button>Button</Button>
<TomatoButton>TomatoButton</TomatoButton>
</div>
);
}
}

在这里,我们用到了类似于ES6中class的extend的语法,无论是在书写美观还是可读性,styled-components都略胜一筹,你的小伙伴也无需看到你class里面装满css的name而头皮发麻啦~

随意更换标签

我们经常会遇到这样的情况,当我们写完一个button的样式后,在一些奇怪的场景下,我们必须要使用a标签来替换button标签,并且a标签和button所要求实现的样式是一样的,这个时候你可能会复制一份button的样式再加到这个a标签中,但是在styled-components中,你可以随意切换标签,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class App extends React.Component {
render() {
const Button = styled.button`
display: inline-block;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const Link = Button.withComponent("a");
return (
<div>
<Button>Normal Button</Button>
<Link>Normal Link</Link>
</div>
);
}
}

继承样式后,通过withComponent来切换标签,随意自如,是那么的优雅~

用props定制主题

我们使用antd的时候,同一个Button通过设置来使用各种样式的按钮,千万不要觉得这很难实现,因为在你自己项目中,自己也能够很方便地是用styled-components的props传参来定制,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class App extends React.Component {
render() {
const Button = styled.button`
background: ${props => (props.primary ? "#188fff" : "white")};
color: ${props => (props.primary ? "white" : "black")};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
`;
return (
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
}
}

设置attrs函数

只是优雅当然不够,还得有html和css的最原始的功能,比如说input里面设置type来定义不同类型的输入框,不知你是否忍受够了在input标签里面书写长长一大串属性(type value class)带来的密集感,要知道如果标签中写的属性过多,会极大地降低html结构的可读性,但是,有了styled-components,一切都可以变得那么优雅~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class App extends React.Component {
render() {
const Input = styled.input.attrs({
type: "password",
placeholder: "A small text input"
})`
color: palevioletred;
border-radius: 3px;
`;
return (
<div>
<Input />
</div>
);
}
}

嵌套和伪类

之所以对styled-components很亲切,是因为他的语法和less有很大的相似之处,并且在styled-components,我们依旧可以使用className来定义样式,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class App extends React.Component {
render() {
const Wrapper = styled.div`
display: block;
h4 {
font-size: 14px;
&:hover {
color: #fff;
}
}
.detail {
color: #ccc;
}
`;
return (
<div>
<Wrapper>
<h4>This is H4</h4>
<p className="detail">this is p</p>
</Wrapper>
</div>
);
}
}

CSS 动画支持

同样动画也是完美支持,和元素CSS写法并没有太大区别,注意要引入styled-components的特定keyframes,代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import styled, { injectGlobal, keyframes } from "styled-components";
class App extends React.Component {
render() {
const fadeIn = keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`;
const FadeInButton = styled.button`
animation: 1s ${fadeIn} ease-out;
`;
return (
<div>
<FadeInButton>button</FadeInButton>
</div>
);
}
}

为什么不污染全局样式

最后我们留下一个问题,我们之前在使用css-modules中,之所以它不会污染全局css样式,是因为构建工具会将类名style.title编译成一个哈希字符串,保证每个class不重复,那么styled-components的原理是怎么样的呢?

我们打开chrome调试工具可以发现:styled-components同样也为class生成了唯一的哈希,并且无需在webpack共建工具中配置任何项目,开箱即用~

image

总结

styled-components在实际开发过程中,我认为最主要还是优化了HTML结构的”美感”,使得代码可读性更好,也提升了组件可维护性和代码复用率,并且引进了很多先进的概念,使得写CSS能够像写JavaScript一样灵活,这对于我们用JavaScript写html的React脑残粉来说,是个“三国统一”的最终愿景,当然这只是styled-components的部分功能,还有更多的功能等待我们去发现,小生学疏才浅,只是抛砖引玉,顺便写一篇博文巩固自己对知识是掌握。