styled-components 备忘清单

此快速参考备忘单提供了使用 CSS in JS 工具的各种方法。

入门

安装

Styled Components 是增强 CSS 在 React 组件系统样式的 CSS in JS 的最佳实践。

安装依赖和 TypeScript 类型依赖

  1. npm install --save styled-components

快速开始

  1. import styled from 'styled-components';

创建一个 Title 组件

  1. // 该组件将呈现具有样式的 <h1> 标签
  2. const Title = styled.h1`
  3. font-size: 1.5em;
  4. text-align: center;
  5. `;

创建一个 Wrapper 组件

  1. // 该组件将呈现具有某些样式的 <section> 标记
  2. const Wrapper = styled.section`
  3. padding: 4em;
  4. background: papayawhip;
  5. `;

像使用其他 React 组件一样使用 Title/Wrapper - 除了它们的样式!

  1. function Demo() {
  2. return (
  3. <Wrapper>
  4. <Title>
  5. Hello World!
  6. </Title>
  7. </Wrapper>
  8. );
  9. }

根据 Props 适配

  1. import styled from 'styled-components';
  2. const Button = styled.button`
  3. /* 根据主要 props 调整颜色 */
  4. background: ${
  5. props =>
  6. props.primary ? "blue" : "white"
  7. };
  8. color: ${
  9. props =>
  10. props.primary ? "white" : "blue"
  11. };
  12. font-size: 1em;
  13. margin: 1em;
  14. padding: 0.25em 1em;
  15. border: 2px solid blue;
  16. border-radius: 3px;
  17. `;

使用 primary props 控制按钮样式

```jsx {5} function Demo() { return (

); }

  1. ### 扩展样式
  2. ```jsx {7}
  3. const Button = styled.button`
  4. color: palevioletred;
  5. border: 2px solid palevioletred;
  6. border-radius: 3px;
  7. `;
  8. // 基于 Button 的新组件,但具有一些覆盖样式
  9. const TomatoButton = styled(Button)`
  10. color: tomato;
  11. border-color: tomato;
  12. `;
  13. const Demo = () => (
  14. <div>
  15. <Button>普通按钮</Button>
  16. <TomatoButton>番茄色按钮</TomatoButton>
  17. </div>
  18. );

扩展样式改变标签 (as)

``jsx {17,20} const Button = styled.button color: palevioletred; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; display: block; `;

const TomatoButton = styled(Button)color: tomato; border-color: tomato;;

const Demo = () => (

番茄按钮样式的链接
);

  1. ### 自定义组件(as)
  2. <!--rehype:wrap-class=row-span-2-->
  3. ```jsx {20}
  4. const Button = styled.button`
  5. color: palevioletred;
  6. font-size: 1em;
  7. border: 2px solid palevioletred;
  8. display: block;
  9. `;
  10. const ReversedButton = props => (
  11. <Button
  12. {...props}
  13. children={
  14. props.children.split('').reverse()
  15. }
  16. />
  17. );
  18. render(
  19. <div>
  20. <Button>普通按钮</Button>
  21. <Button as={ReversedButton}>
  22. 具有普通按钮样式的自定义按钮
  23. </Button>
  24. </div>
  25. );

样式化任何组件

  1. const Link = ({ className, children }) => (
  2. <a className={className}>
  3. {children}
  4. </a>
  5. );
  6. const StyledLink = styled(Link)`
  7. color: palevioletred;
  8. font-weight: bold;
  9. `;
  10. <StyledLink className="hello" />

在 render 之外定义 Styled 组件

``jsx {3} const Box = styled.div//`; const Wrapper = ({ message }) => { // ⚠️ 不能在这里定义 styled 组件 return ( {message} ); };

  1. 注意:组件 `Box` 不能放到 `Wrapper` 函数组件里面
  2. ### 传入值
  3. ```jsx {3,4,17}
  4. const Input = styled.input`
  5. color: ${
  6. props =>
  7. props.inputColor || "palevioletred"
  8. };
  9. background: papayawhip;
  10. `;
  11. const Demo = () => (
  12. <div>
  13. <Input
  14. defaultValue="@probablyup"
  15. type="text"
  16. />
  17. <Input
  18. defaultValue="@geelen"
  19. type="text"
  20. inputColor="rebeccapurple"
  21. />
  22. </div>
  23. );

样式对象

```jsx {2,5} const PropsBox = styled.div(props => ({ background: props.background, height: ‘50px’, width: ‘50px’, fontSize: ‘12px’ }));

  1. 在组件中使用
  2. ```jsx {5}
  3. const Example = () => {
  4. return (
  5. <div>
  6. <PropsBox
  7. background="blue"
  8. />
  9. </div>
  10. );
  11. }

注意:样式对象里面的样式并不是 CSS 中的写法。

CSSModules => styled

  1. import React, { useState } from 'react';
  2. import styles from './styles.css';
  3. function ExampleCounter() {
  4. const [count, setCount] = useState(0)
  5. return (
  6. <div className={styles.counter}>
  7. <p className={styles.paragraph}>
  8. {count}
  9. </p>
  10. <button
  11. className={styles.button}
  12. onClick={() => setCount(count +1)}
  13. >
  14. +
  15. </button>
  16. <button
  17. className={styles.button}
  18. onClick={() => setCount(count -1)}
  19. >
  20. -
  21. </button>
  22. </div>
  23. );
  24. }

👇👇 与下面 styled 写法等效 👇👇

  1. import styled from 'styled-components';
  2. const StyledCounter = styled.div`
  3. /* ... */
  4. `;
  5. const Paragraph = styled.p`
  6. /* ... */
  7. `;
  8. const Button = styled.button`
  9. /* ... */
  10. `;
  11. function ExampleCounter() {
  12. const [count, setCount] = useState(0);
  13. const increment = () => {
  14. setCount(count +1);
  15. }
  16. const decrement = () => {
  17. setCount(count -1);
  18. }
  19. return (
  20. <StyledCounter>
  21. <Paragraph>{count}</Paragraph>
  22. <Button onClick={increment}>
  23. +
  24. </Button>
  25. <Button onClick={decrement}>
  26. -
  27. </Button>
  28. </StyledCounter>
  29. );
  30. }

伪元素、伪选择器和嵌套

``jsx {3,6,9,12,15} const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 })) color: blue; &:hover { / 悬停时 / color: red; } & ~ & { / 作为 的兄弟,但可能不直接在它旁边 / background: tomato; } & + & { / 旁边的 / background: lime; } &.something { / 标记有一个额外的 CSS 类 “.something” / background: orange; } .something-else & { / 在另一个标记为 “.something-else” 的元素中 / border: 1px solid; } `;

render( Hello world! 你怎么样? 艳阳高照…

今天真是美好的一天。
你不觉得吗?
灿烂
);

  1. ### 改变 styled 组件样式
  2. <!--rehype:wrap-class=row-span-2-->
  3. ```jsx {13,21}
  4. import { css } from 'styled-components'
  5. import styled from 'styled-components'
  6. const Input = styled.input.attrs({
  7. type: "checkbox"
  8. })``;
  9. const LabelText = styled.span`
  10. ${(props) => {
  11. switch (props.$mode) {
  12. case "dark":
  13. return css`
  14. color: white;
  15. ${Input}:checked + && {
  16. color: blue;
  17. }
  18. `;
  19. default:
  20. return css`
  21. color: black;
  22. ${Input}:checked + && {
  23. color: red;
  24. }
  25. `;
  26. }
  27. }}
  28. `;
  29. function Example() {
  30. return (
  31. <React.Fragment>
  32. <Label>
  33. <Input defaultChecked />
  34. <LabelText>Foo</LabelText>
  35. </Label>
  36. <Label>
  37. <Input />
  38. <LabelText $mode="dark">
  39. Foo
  40. </LabelText>
  41. </Label>
  42. </React.Fragment>
  43. );
  44. }

全局样式 createGlobalStyle

```jsx {3,11} import { styled, createGlobalStyle } from ‘styled-components’

const Thing = styled.div&& { color: blue; }; const GlobalStyle = createGlobalStylediv${Thing} { color: red; };

const Example = () => ( 我是蓝色的 );

  1. ### className 使用
  2. ```JSX
  3. const Thing = styled.div`
  4. color: blue;
  5. /* <Thing> 中标记为“.something”的元素 */
  6. .something {
  7. border: 1px solid;
  8. }
  9. `;
  10. function Example() {
  11. return (
  12. <Thing>
  13. <label
  14. htmlFor="foo-button"
  15. className="something"
  16. >
  17. 神秘按钮
  18. </label>
  19. <button id="foo-button">
  20. 我该怎么办?
  21. </button>
  22. </Thing>
  23. )
  24. }

共享样式片段

  1. const rotate = keyframes`
  2. from {top:0px;}
  3. to {top:200px;}
  4. `;
  5. // ❌ 这将引发错误!
  6. const styles = `
  7. animation: ${rotate} 2s linear infinite;
  8. `;
  9. // ✅ 这将按预期工作
  10. const styles = css`
  11. animation: ${rotate} 2s linear infinite;
  12. `;

Class 组件样式定义

`jsx {5} class NewHeader extends React.Component { render() { return ( <div className={this.props.className} /> ); } } const StyledA = styled(NewHeader) const Box = styled.div${StyledA} { /* 变更 NewHeader 样式 */ };

  1. ### 附加额外的 Props
  2. ```jsx {3,5,13,14,23}
  3. const Input = styled.input.attrs(props=>({
  4. // 我们可以定义静态道具
  5. type: "text",
  6. // 或者我们可以定义动态的
  7. size: props.size || "1em",
  8. }))`
  9. color: palevioletred;
  10. font-size: 1em;
  11. border: 2px solid palevioletred;
  12. border-radius: 3px;
  13. /* 这里我们使用动态计算的 props */
  14. margin: ${props => props.size};
  15. padding: ${props => props.size};
  16. `;

使用 Input 组件

  1. function Example() {
  2. return (
  3. <div>
  4. <Input placeholder="小文本输入" />
  5. <br />
  6. <Input
  7. placeholder="更大的文本输入"
  8. size="2em"
  9. />
  10. </div>
  11. )
  12. }

覆盖 .attrs

``jsx {11} const Input = styled.input.attrs(props=>({ type: "text", size: props.size || "1em", })) border: 2px solid palevioletred; margin: ${props => props.size}; padding: ${props => props.size}; ; // Input 的attrs会先被应用,然后这个 attrs obj const PasswordInput = styled(Input).attrs({ type: "password", }) / 同样,border 将覆盖 Input 的边框 / border: 2px solid aqua; `;

  1. 使用 `Input` `PasswordInput` 组件
  2. ```jsx {5,11}
  3. render(
  4. <div>
  5. <Input
  6. placeholder="更大的文本输入"
  7. size="2em"
  8. />
  9. <br />
  10. {/*⚠️ 仍然可以使用Input中的 size attr*/}
  11. <PasswordInput
  12. placeholder="更大的密码输入"
  13. size="2em"
  14. />
  15. </div>
  16. );

动画

创建关键帧

  1. const rotate = keyframes`
  2. from {
  3. transform: rotate(0deg);
  4. }
  5. to {
  6. transform: rotate(360deg);
  7. }
  8. `;

我们创建一个 Rotate 组件

  1. // 它将在两秒内旋转我们传递的所有内容
  2. const Rotate = styled.div`
  3. display: inline-block;
  4. animation: ${rotate} 2s linear infinite;
  5. padding: 2rem 1rem;
  6. font-size: 1.2rem;
  7. `;

使用 Rotate 组件

  1. function Example() {
  2. return (
  3. <Rotate>&lt; 💅🏾 &gt;</Rotate>
  4. )
  5. }

isStyledComponent

  1. import React from 'react'
  2. import styled, { isStyledComponent } from 'styled-components'
  3. import MaybeStyledComponent from './my'
  4. let TargetedComponent = isStyledComponent(MaybeStyledComponent)
  5. ? MaybeStyledComponent
  6. : styled(MaybeStyledComponent)``;
  7. const ParentComponent = styled.div`
  8. color: cornflowerblue;
  9. ${TargetedComponent} {
  10. color: tomato;
  11. }
  12. `;

ThemeConsumer

  1. import {
  2. ThemeConsumer
  3. } from 'styled-components'
  4. function Example() {
  5. return (
  6. <ThemeConsumer>
  7. {theme => (
  8. <div>主题色是 {theme.color}</div>
  9. )}
  10. </ThemeConsumer>
  11. );
  12. }

TypeScript

安装

Web 应用上安装 styled

  1. npm install -D @types/styled-components

React Native 应用上安装 styled

  1. npm install -D \
  2. @types/styled-components \
  3. @types/styled-components-react-native

如果对 TypeScript 不熟悉,参考 TypeScript 备忘清单

自定义 Props

  1. import styled from 'styled-components';
  2. interface TitleProps {
  3. readonly isActive: boolean;
  4. }
  5. const Title = styled.h1<TitleProps>`
  6. color: ${(props) => (
  7. props.isActive
  8. ? props.theme.colors.main
  9. : props.theme.colors.secondary
  10. )};
  11. `;

简单的 Props 类型定义

  1. import styled from 'styled-components';
  2. import Header from './Header';
  3. const Header = styled.header`
  4. font-size: 12px;
  5. `;
  6. const NewHeader = styled(Header)<{
  7. customColor: string;
  8. }>`
  9. color: ${(props) => props.customColor};
  10. `;

禁止转移到子组件($)

```tsx {5} import styled from ‘styled-components’; import Header from ‘./Header’;

interface ReHeader { $customColor: string; }

const ReHeader = styled(Header)color: ${ props => props.$customColor };;

  1. 禁止 `customColor` 属性转移到 `Header` 组件,在其前面加上美元(`$`)符号
  2. ### 函数组件类型继承
  3. <!--rehype:wrap-class=col-span-2-->
  4. ```tsx {8,13}
  5. import { FC, PropsWithRef, DetailedHTMLProps, ImgHTMLAttributes } from 'react';
  6. import styled from 'styled-components';
  7. const Img = styled.img`
  8. height: 32px;
  9. width: 32px;
  10. `;
  11. export interface ImageProps extends DetailedHTMLProps<
  12. ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement
  13. > {
  14. text?: string;
  15. };
  16. export const Image: FC<PropsWithRef<ImageProps>> = (props) => (
  17. <Img src="" alt="" {...props} />
  18. );

React Native

基础实例

  1. import React from 'react'
  2. import styled from 'styled-components/native'
  3. const StyledView = styled.View`
  4. background-color: papayawhip;
  5. `;
  6. const StyledText = styled.Text`
  7. color: palevioletred;
  8. `;
  9. class MyReactNativeComponent extends React.Component {
  10. render() {
  11. return (
  12. <StyledView>
  13. <StyledText>Hello World!</StyledText>
  14. </StyledView>
  15. );
  16. }
  17. }

React Native 中写 CSS

  1. import styled from 'styled-components/native'
  2. const RotatedBox = styled.View`
  3. transform: rotate(90deg);
  4. text-shadow-offset: 10px 5px;
  5. font-variant: small-caps;
  6. margin: 5px 7px 2px;
  7. `;
  8. function Example() {
  9. return (
  10. <RotatedBox />
  11. )
  12. }

与 web 版本的一些区别是,您不能使用关键帧(keyframes)和 createGlobalStyle 助手,因为 React Native 不支持关键帧或全局样式。如果您使用媒体查询或嵌套 CSS,我们也会警告您。

高级用法

主题化

  1. import styled, { ThemeProvider } from 'styled-components'
  2. // 定义我们的按钮,但这次使用 props.theme
  3. const Button = styled.button`
  4. font-size: 1em;
  5. margin: 1em;
  6. padding: 0.25em 1em;
  7. border-radius: 3px;
  8. /* 使用 theme.main 为边框和文本着色 */
  9. color: ${props => props.theme.main};
  10. border: 2px solid ${props => props.theme.main};
  11. `;
  12. // 我们正在为未包装在 ThemeProvider 中的按钮传递默认主题
  13. Button.defaultProps = {
  14. theme: {
  15. main: "palevioletred"
  16. }
  17. }
  18. // 定义 props.theme 的外观
  19. const theme = {
  20. main: "mediumseagreen"
  21. };
  22. render(
  23. <div>
  24. <Button>Normal</Button>
  25. <ThemeProvider theme={theme}>
  26. <Button>Themed</Button>
  27. </ThemeProvider>
  28. </div>
  29. );

功能主题

  1. import styled, { ThemeProvider } from 'styled-components'
  2. // 定义我们的按钮,但这次使用 props.theme
  3. const Button = styled.button`
  4. color: ${props => props.theme.fg};
  5. border: 2px solid ${props => props.theme.fg};
  6. background: ${props => props.theme.bg};
  7. font-size: 1em;
  8. margin: 1em;
  9. padding: 0.25em 1em;
  10. border-radius: 3px;
  11. `;
  12. // 在主题上定义我们的`fg`和`bg`
  13. const theme = {
  14. fg: "palevioletred",
  15. bg: "white"
  16. };
  17. // 这个主题交换了`fg`和`bg`
  18. const invertTheme = ({ fg, bg }) => ({
  19. fg: bg,
  20. bg: fg
  21. });
  22. render(
  23. <ThemeProvider theme={theme}>
  24. <div>
  25. <Button>默认主题</Button>
  26. <ThemeProvider theme={invertTheme}>
  27. <Button>反转主题</Button>
  28. </ThemeProvider>
  29. </div>
  30. </ThemeProvider>
  31. );

通过 withTheme 高阶组件

  1. import { withTheme } from 'styled-components'
  2. class MyComponent extends React.Component {
  3. render() {
  4. console.log('Current theme: ', this.props.theme)
  5. // ...
  6. }
  7. }
  8. export default withTheme(MyComponent)

useContext 钩子

  1. import { useContext } from 'react'
  2. import { ThemeContext } from 'styled-components'
  3. const MyComponent = () => {
  4. const themeContext = useContext(ThemeContext)
  5. console.log('Current theme: ', themeContext)
  6. // ...
  7. }

useTheme 自定义钩子

  1. import {useTheme} from 'styled-components'
  2. const MyComponent = () => {
  3. const theme = useTheme()
  4. console.log('Current theme: ', theme)
  5. // ...
  6. }

主题 props

  1. import {
  2. ThemeProvider,
  3. styled
  4. } from 'styled-components';
  5. // 定义我们的按钮
  6. const Button = styled.button`
  7. font-size: 1em;
  8. margin: 1em;
  9. padding: 0.25em 1em;
  10. /* 使用 theme.main 为边框和文本着色 */
  11. color: ${props => props.theme.main};
  12. border: 2px solid ${props => props.theme.main};
  13. `;
  14. // 定义主题的外观
  15. const theme = {
  16. main: "mediumseagreen"
  17. };

使用自定义主题组件

  1. render(
  2. <div>
  3. <Button theme={{ main: "royalblue" }}>
  4. 特设主题
  5. </Button>
  6. <ThemeProvider theme={theme}>
  7. <div>
  8. <Button>Themed</Button>
  9. <Button
  10. theme={{ main: "darkorange" }}
  11. >
  12. 被覆盖
  13. </Button>
  14. </div>
  15. </ThemeProvider>
  16. </div>
  17. );

Refs

  1. import {
  2. ThemeProvider,
  3. styled
  4. } from 'styled-components';
  5. const Input = styled.input`
  6. border: none;
  7. border-radius: 3px;
  8. `;
  9. class Form extends React.Component {
  10. constructor(props) {
  11. super(props);
  12. this.inputRef = React.createRef();
  13. }
  14. render() {
  15. return (
  16. <Input
  17. ref={this.inputRef}
  18. placeholder="Hover to focus!"
  19. onMouseEnter={() => {
  20. this.inputRef.current.focus()
  21. }}
  22. />
  23. );
  24. }
  25. }

使用 Form 组件

  1. function Example() {
  2. return (
  3. <Form />
  4. )
  5. }

特异性问题

在文件 MyComponent.js 中定义 MyComponent 组件。

  1. const MyComponent = styled.div`
  2. background-color: green;
  3. `;

定义样式 my-component.css

  1. .red-bg {
  2. background-color: red;
  3. }

使用 MyComponent 组件

  1. <MyComponent className="red-bg" />

由于某种原因,这个组件仍然有绿色背景,即使你试图用 red-bg 类覆盖它!

解决方案

  1. .red-bg.red-bg {
  2. background-color: red;
  3. }

ThemeProvider

  1. import styled, { ThemeProvider } from 'styled-components'
  2. const Box = styled.div`
  3. color: ${props => props.theme.color};
  4. `;
  5. const Example = () => (
  6. <ThemeProvider theme={{ color: 'mediumseagreen' }}>
  7. <Box>I'm mediumseagreen!</Box>
  8. </ThemeProvider>
  9. );

shouldForwardProp

  1. const Comp = styled('div').withConfig({
  2. shouldForwardProp: (prop, defaultValidatorFn) =>
  3. !['hidden'].includes(prop) && defaultValidatorFn(prop),
  4. }).attrs({ className: 'foo' })`
  5. color: red;
  6. &.foo {
  7. text-decoration: underline;
  8. }
  9. `;
  10. const Example = () => (
  11. <Comp hidden draggable="true">
  12. Drag Me!
  13. </Comp>
  14. );