team
team copied to clipboard
styled-components 样式组件
styled-components
背景
- 使用React的过程中,组件通过 JS 来定义 css 样式,就必须在 React 组件里面通过style属性来写。
export class App extends React.Component {
render() {
return(
<div style={{marginTop: '10px'}}>
......
</div>
);
}
}
缺点:
(1)样式属性与原生 css 的写法不一致,样式名称必须用驼峰的命名方式来命令,而不是多个单词用-号分隔;单位的写法也不一致,百分比是用字符串形式。 (2)无法写伪元素,要实现hover的话就必须使用onMouseOver事件来实现,而且要写很多 JS 代码,像before和after这类伪元素则根本没法通过这种方式实现。
为了顺应组件化的潮流,人们开始考虑使用JS上编写CSS,styled components就是其中一种解决方案。styled components是一个React第三方库,作用是可以将样式写成组件的形式,实现在JS上编写CSS
基本用法
- 使用styled-components不需要再使用className属性来控制样式,而是将样式写成更具语义化的组件的形式,如下例:
import React from 'react';
import styled from 'styled-components';
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: red;
`;
class App extends React.Component {
render() {
return (
<Title>Hello styled--components</Title>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
示例:
上例中的styled.h1是一个标签模板函数,紧跟其后的是一个模板字符串参数,“标签模板”和“模板字符串”都是es6的语法。styled.h1函数返回一个React Component,styled components会为这个React Component添加一个class,该class的值为一个
随机字符串
。传给styled.h1的模板字符串参数的值实际上是CSS语法,这些CSS会附加到该React Component的class中,从而为React Component添加样式。
props参数控制样式
- 在React中是通过控制className和style来控制样式的,如下例,App组件需要判断是否有primary属性来判断是否渲染”title-primary”类的样式:
export class App extends React.Component {
render() {
let className = this.props.primary ? "title-primary" : '';
return(
<div className={className}>
...
</div>
);
}
}
styled components使用props来控制样式,将控制样式代码放在样式组件中,使React组件更加简洁:
const Title = styled.div`
font-size: 1.5em;
text-align: center;
color: ${props => props.primary ? 'blue' : ' red'};
`
export class App extends React.Component {
render() {
return (
<Title primary>
Hello Styled-Component
</Title>
)
}
}
styled components的写法中样式组件暴露props让外层JS来控制样式,不再需要className或style这样的“中间人”,移除了样式和组件间的映射关系。
CSS作用域
- CSS有一个痛点——CSS的作用域是全局的。当两个CSS选择器有冲突时,会根据选择器的权值确定使用哪一个选择选择器。当项目较大时,编写的css选择器很难判断会不会把另外一个选择器冲掉。 解决CSS作用域的其中一个方法就是使用后代选择器,这种用法和命名空间相似,即在每个选择器前添加一个父元素的选择器,从而减少选择器冲突的概率。 使用styled components会给生成的React组件添加一个值为随机字符串的className。使用同一个styled components生成的多个React组件的className是不同的,这种随机className的机制使得组件之间的className值不会冲突,从而解决了CSS全局作用域的问题。
styled components也支持css的嵌套语法:
const Title = styled.div`
font-size: 1.5em;
text-align: center;
.content1 {
color: #f16da7;
margin: 10px;
}
.content2 {
color: #98a514;
}
`;
export class App extends React.Component {
render() {
return (
<Title>
<div className="content1">content1-style</div>
<div className="content2">content2-style</div>
</Title>
)
}
}
示例
组件样式继承
- 通常在 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;
`;
export class App extends React.Component {
render() {
return (
<React.Fragment>
<Button>
<TomatoButton>styled-components</TomatoButton>
</Button>
</React.Fragment>
)
}
}
示例
我们可以看到在原生的样式和组件之间是多对多的映射关系,styled-components 通过继承彻底的移除了这种只为了样式而存在映射关系。组件内部会执行类似于对象合并的操作,子组件中的属性会覆盖父组件中同名的属性。
styled-components中的 attrs
styled-components 同时支持为组件传入 html 元素的其他属性,比如为 input 元素指定一个 type 属性,我们可以使用 attrs 方法来完成:
const Password = styled.input.attrs({
type: 'password'
})`
border: 1px solid #ccc;
padding: 7px 0px;
border-radius: 3px;
padding-left:5px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
`;
export class App extends React.Component {
render() {
return (
<React.Fragment>
<Password name="pwd"/>
</React.Fragment>
)
}
}
示例
在实际开发中,这个方法还有一个有用处,用来引用第三方类库的 css 样式和自己定义的类名:
const Row = styled.div.attrs({className: "row"})`
color: palevioletred;
font-size: 1em;
margin: 1em;
`
export class App extends React.Component {
render() {
return (
<React.Fragment>
<Row>
hello styled-components
</Row>
</React.Fragment>
)
}
}
示例
theme 特性
- styled-components 里面还有一个比较特别的特性是theme,它利用了 React 的context特性来传递其中的参数,可以让样式对象直接从最上层的组件传递到最下层的组件。
const Box = styled.div`
color: ${props => props.theme.color};
`;
export class App extends React.Component {
render() {
return (
<React.Fragment>
<ThemeProvider theme={{ color: 'mediumseagreen' }}>
<Box>sI'm mediumseagreen!</Box>
</ThemeProvider>
</React.Fragment>
)
}
}
示例
独立样式文件
在一般的 React 组件中,样式和组件文件是分离的(比如 App.js 和 style.css),使用了 styled-components 后其实也可以沿用这种方式来组织文件,定义一个style.js文件来写样式对象,然后在组件文件中引入该样式文件。
// style.js
import styled from 'styled-components';
export const AppDiv = styled.div`
color: palevioletred;
font-size: 1em;
margin: 1em;
`;
// App.js
import React from 'react';
import * as css from './style';
export class App extends React.Component {
render() {
return (
<React.Fragment>
<css.AppDiv>Hello styled-components</css.AppDiv>
</React.Fragment>
)
}
}
总结
- 可以在样式定义中直接引用到 js 变量,共享变量,非常便利;
- 支持组件之间继承,方便代码复用,提升可维护性;
- 兼容现有的 className 方式,升级无痛;
这篇文章有点老了,现在已经不使用 .extend 这个方法了。