React之css解决方案Styled-Components
感谢 Liang Wang 提供中文翻译文档
https://github.com/hengg/styled-components-docs-zh/blob/master/Basics.md
我在这里精简一下基础部分,方便快速回顾知识,没学过的还是建议先看文档
安装 yarn add styled-components
引入 import styled from 'styled-components'
先来个最基础的例子
// 创建一个 Title 组件,它将渲染一个附加了样式的 <h1> 标签
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// 创建一个 Wrapper 组件,它将渲染一个附加了样式的 <section> 标签
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// 就像使用常规 React 组件一样使用 Title 和 Wrapper
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
先解析第一行 styled
是引入的styled,.
后面的是你要创建的标签名,` `
中间包裹的就是正常的CSS样式(可以写变量,传props值,后面会讲到),然后你这些复制给一个大写开头的Title变量,那么Title在styled的作用下就变成了一个组件,直接把组件渲染出来就可以了
基于属性的适配
const Button = styled.button`
/* Adapt the colors based on primary prop */
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>
);
样式继承
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// 一个继承 Button 的新组件, 重载了一部分样式
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
多态 "as" polymorphic prop
const Button = styled.button`
display: inline-block;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<Button as="a" href="/">Link with Button styles</Button>
<TomatoButton as="a" href="/">Link with Tomato Button styles</TomatoButton>
</div>
);
这也完美适用于自定义组件:
const Button = styled.button`
display: inline-block;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
const ReversedButton = props => <button {...props} children={props.children.split('').reverse()} />
render(
<div>
<Button>Normal Button</Button>
<Button as={ReversedButton}>Custom Button with Normal Button styles</Button>
</div>
);
给任何组件添加样式
styled方法适用于任何最终向 DOM 元素传递 className 属性的组件,当然也包括第三方组件.
注意
在 react-native 中,请使用 style 而不是 className.
// 下面是给 react-router-dom Link 组件添加样式的示例
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
);
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
render(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
);
也可以传递标签给styled(), 比如: styled("div"). 实际上styled.tagname的方式就是 styled(tagname)`的别名.
属性传递
const Input = styled.input`
padding: 0.5em;
margin: 0.5em;
color: ${props => props.inputColor || "palevioletred"};
background: papayawhip;
border: none;
border-radius: 3px;
`;
// 渲染两个样式化的 text input,一个标准颜色,一个自定义颜色
render(
<div>
<Input defaultValue="@probablyup" type="text" />
<Input defaultValue="@geelen" type="text" inputColor="rebeccapurple" />
</div>
);
注意, inputColor prop并没有传递给 DOM, 但是type和defaultValue 都传递了. styled-components足够智能,会自动过滤掉所有非标准 attribute.
styled-components 如何在组件中工作?
Styled Component 是 HTML 元素和作用在元素上的样式规则的组合, 我们可以这样编写Counter:
import React from 'react'
import styled from 'styled-components'
const StyledCounter = styled.div`
/* ... */
`
const Paragraph = styled.p`
/* ... */
`
const Button = styled.button`
/* ... */
`
export default class Counter extends React.Component {
state = { count: 0 }
increment = () => this.setState({ count: this.state.count + 1 })
decrement = () => this.setState({ count: this.state.count - 1 })
render() {
return (
<StyledCounter>
<Paragraph>{this.state.count}</Paragraph>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
</StyledCounter>
)
}
}
伪元素,伪类选择器和嵌套
styled-component
所使用的预处理器stylis支持自动嵌套的 scss-like 语法,示例如下:
const Thing = styled.div`
color: blue;
`
伪元素和伪类无需进一步细化,而是自动附加到了组件:
const Thing = styled.button`
color: blue;
::before {
content: '🚀';
}
:hover {
color: red;
}
`
render(
<Thing>Hello world!</Thing>
)
对于更复杂的选择器,可以使用与号(&)来指向主组件.以下是一些示例:
const Thing = styled.div.attrs({ tabIndex: 0 })`
color: blue;
&:hover {
color: red; // <Thing> when hovered
}
& ~ & {
background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
}
& + & {
background: lime; // <Thing> next to <Thing>
}
&.something {
background: orange; // <Thing> tagged with an additional CSS class ".something"
}
.something-else & {
border: 1px solid; // <Thing> inside another element labeled ".something-else"
}
`
如果只写选择器而不带&,则指向组件的子节点.
const Thing = styled.div`
color: blue;
.something {
border: 1px solid; // an element labeled ".something" inside <Thing>
display: block;
}
`
render(
<Thing>
<label htmlFor="foo-button" className="something">Mystery button</label>
<button id="foo-button">What do I do?</button>
</Thing>
)
附加额外的属性 (v2)
为了避免仅为传递一些props来渲染组件或元素而使用不必要的wrapper, 可以使用 .attrs
constructor. 通过它可以添加额外的 props 或 attributes 到组件.
举例来说,可以通过这种方式给元素添加静态 props,或者传递第三方 prop 给组件(比如传递activeClassName
给 React Router 的 Link
). 此外也可以将dynamic props 添加到组件. .attrs
对象也接收函数,返回值也将合并进 props.
示例如下:
source-js-jsx
const Input = styled.input.attrs({
// static props
type: "password",
// dynamic props
margin: props => props.size || "1em",
padding: props => props.size || "1em"
})`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;
/* dynamically computed props */
margin: ${props => props.margin};
padding: ${props => props.padding};
`;
render(
<div>
<Input placeholder="A small text input" size="1em" />
<br />
<Input placeholder="A bigger text input" size="2em" />
</div>
);
动画
虽然使用@keyframes的 CSS 动画不限于单个组件,但我们仍希望它们不是全局的(以避免冲突). 这就是为什么 styled-components 导出 keyframes helper 的原因: 它将生成一个可以在 APP 应用的唯一实例:
// Create the keyframes
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div`
display: inline-block;
animation: ${rotate} 2s linear infinite;
padding: 2rem 1rem;
font-size: 1.2rem;
`;