styled-components 是一个常用的 css in js 类库。和所有同类型的类库一样,通过 js 赋能解决了原生 css 所不具备的能力,比如变量、循环、函数等。诸如 sass&less 等预处理可以解决部分 css 的局限性,但还是要学习新的语法,而且需要对其编译,其复杂的 webpack 配置也总是让开发者抵触。而 styled-componens 很好的解决了这些问题,很适合 React 技术栈的项目开发。
安装和使用
下面我们看一个简单的示例:
$ npm install styled-components
$ yarn add styled-components
我们可以用自己喜欢的方式将包安装到本地,然后就可以在项目中直接使用它:
import styled from 'styled-components';const Wrapper = styled.section` margin: 0 auto; width: 300px; text-align: center;`;const Button = styled.button` width: 100px; color: white; background: skyblue;`;render(
<Wrapper>
<Button>Hello World</Button>
</Wrapper>);
上述的代码片段展示了 React 项目中使用 styled-components,定义了 Wrapper 和 Button 两个组件,包含了 html 结构和对应的 css 样式,从而将样式和组件之间的 class 映射关系移除。这种用法的学习成本还是很少的,在实际应用中我们完全可以将 style 和 jsx 分开维护。
组件和容器
在 styled-components 中, 将最简单只包含样式的结构称为组件(components),将包含业务逻辑或者复杂生命周期方法(无样式)的称之为容器(containers)。同时,将组件和容器分离开。
下面的 2 个代码片段简单的说明了是否使用 styled-components 的区别:
1.没有使用 styled-components
class Sidebar extends React.Component {
componentDidMount() {
fetch('/url')
.then(res => {
this.setState({
items: res.items,
})
})
}
render() {
return (
<div classNames="sidebar">
{this.state.items.map(item => (
<div className="sidebar__item">{item}</div>
))}
</div>
);
}}
2. 使用 styled-component
class SidebarContainer extends React.Component {
componentDidMount() {
fetch('/url')
.then(res => {
this.setState({
items: res.items,
})
})
}
render() {
return (
<Sidebar>
{this.state.items.map(item => (
<SidebarItem>{item}</SidebarItem>
))}
</Sidebar>
);
}}
基于 props 定制主题
下面来看一下使用 styled-components 来定义组件的主题。
const Button = styled.button` background: ${props => props.primary ? 'palevioletred' : 'white'}; color: ${props => props.primary ? 'white' : 'palevioletred'}; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px;`;render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>);
我们在组件中传入的所有 props 都可以在定义组件时获取到,这样就可以很容易实现组件主题的定制。如果没有 styled-components 的情况下,需要使用组件 style 属性或者定义多个 class 的方式来实现。
组件样式继承
通常在 css 中一般会通过给 class 传入多个 name 通过空格分隔的方式来复用 class 定义,类似 class="button tomato"。在 styled-components 中利用了 js 的继承实现了这种样式的复用:
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;
我们可以看到在原生的样式和组件之间是多对多的映射关系,styled-components 通过继承彻底的移除了这种只为了样式而存在映射关系。组件内部会执行类似于对象合并的操作,子组件中的属性会覆盖父组件中同名的属性。
组件内部使用 className
在日常开发中总会出现覆盖组件内部样式的需求,你可能想在 styled-components 中使用 className,或者在使用第三方组件时。看下面的示例:
<Wrapper>
<h4>Hello Wordh4>
<div className="detail">div>
Wrapper>
对于这种 styled-components 和 className 混用,或者是一些伪类的情况同样是支持的:
import styled from 'styled-components';
const Wrapper = styled.div`
display: block;
h4 {
font-size: 14px;
&:hover {
color: #fff;
}
}
.detail { color: #ccc; }
`;
当然还可以通过 injectGlobal 的方式将通用的样式注入到全局中:
import styled, { injectGlobal } from 'styled-components';
injectGlobal`
@font-face {
font-family: 'Operator Mono';
src: url('../fonts/Operator-Mono.ttf');
}
body {
margin: 0;
}
`;
组件中维护其他属性