基础

动机

styled-components 是对如何增强 CSS 以为 React 组件系统添加样式的思考结果。 通过专注于单一用例,我们成功优化了开发人员的体验以及最终用户的输出。

除了改善开发人员的体验之外,styled-components 还提供了

  • 自动关键 CSS:styled-components 会跟踪页面上渲染了哪些组件,并仅注入它们的样式,不会注入其他任何内容,完全自动完成。结合代码拆分,这意味着您的用户仅加载必要的最少代码。
  • 没有类名错误:styled-components 为您的样式生成唯一的类名。您无需担心重复、重叠或拼写错误。
  • 更轻松地删除 CSS:很难知道某个类名是否在您的代码库中的某个地方使用过。styled-components 使其变得显而易见,因为每一部分样式都与特定组件绑定。如果组件未被使用(工具可以检测到这一点)并被删除,则所有与其相关的样式都会被删除。
  • 简单的动态样式:根据组件的 props 或全局主题调整组件的样式非常简单直观,无需手动管理数十个类。
  • 轻松维护:您无需在不同的文件中来回搜索以查找影响您组件的样式,因此无论您的代码库有多大,维护都轻而易举。
  • 自动供应商前缀:按照当前标准编写您的 CSS,让 styled-components 处理其余部分。

您将获得所有这些优势,同时仍然编写您熟悉和喜爱的 CSS,只是绑定到单个组件。

安装

安装 styled-components 只需要一个命令,您就可以开始使用它

# with npm
npm install styled-components

# with yarn
yarn add styled-components

如果您使用的是像 yarn 这样的包管理器,它支持 "resolutions" package.json 字段,我们还强烈建议您也添加一个与主要版本范围相对应的条目。这有助于避免由于在项目中安装了多个版本的 styled-components 而引起的一系列问题。

package.json

{
  "resolutions": {
    "styled-components": "^5"
  }
}
注意

强烈建议(但不是必需)使用 Babel 插件。它提供了许多优势,例如更易读的类名、服务器端渲染兼容性、更小的包等等。

单击此处查看其他 CDN 安装说明

如果您没有使用模块打包器或包管理器,我们还提供了一个托管在 unpkg CDN 上的全局(“UMD”)构建。只需将以下 <script> 标签添加到 HTML 文件的底部

<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>

添加 styled-components 后,您将可以访问全局 window.styled 变量。

const Component = window.styled.div`
  color: red;
`
注意

这种使用方式需要 react CDN 包react-is CDN 包 也在页面上(在 styled-components 脚本之前)。

入门

styled-components 使用带标签的模板字面量来为您的组件添加样式。

它消除了组件和样式之间的映射。这意味着当您定义样式时,实际上是在创建一个普通的 React 组件,该组件附加了您的样式。

此示例创建了两个简单的组件,一个包装器和一个标题,以及附加到它的某些样式

Hello World!

这是一个实时编辑器,因此您可以尝试修改代码,以了解使用 styled-components 的感觉!

注意

CSS 规则会自动添加供应商前缀,styled-components 会为您处理!

样式化的组件在后台使用 stylis.js 包来添加 CSS 规则的前缀。

有关 stylis.js 中支持的前缀的更多信息,请访问其 存储库

根据 props 调整样式

您可以将函数(“插值”)传递给样式化组件的模板字面量,以根据其 props 调整它。

此按钮组件具有一个主要状态,该状态会更改其颜色。将 $primary prop 设置为 true 时,我们正在交换其背景色和文本颜色。

扩展样式

您可能经常想要使用一个组件,但将其稍微更改以适应特定情况。现在,您可以传递一个插值函数并根据某些 props 进行更改,但这对于覆盖一次样式来说太麻烦了。

为了轻松地创建一个继承另一个组件样式的新组件,只需将其包装在 styled() 构造函数中即可。这里我们使用上一节中的按钮,并创建一个特殊的按钮,通过一些与颜色相关的样式来扩展它

我们可以看到,新的 TomatoButton 仍然类似于 Button,而我们只添加了两个新规则。

在某些情况下,您可能想要更改样式化组件渲染的标签或组件。例如,在构建导航栏时,这很常见,因为其中包含链接和按钮的混合,但它们应该具有相同的样式。

针对这种情况,我们有一个逃生舱。您可以使用 "as" 多态 prop 动态地交换接收您编写的样式的元素

这对于自定义组件也能完美地工作!

为任何组件添加样式

styled 方法适用于您自己的所有组件或任何第三方组件,只要它们将传递的 className prop 附加到 DOM 元素即可。

注意

如果您使用的是 react-native,请记住使用 style 而不是 className

注意

您也可以将标签名称传递到 styled() 工厂调用中,如下所示:styled("div")。实际上,styled.tagname 助手只是执行相同操作的别名。

传递 props

如果样式化的目标是简单元素(例如 styled.div),styled-components 会将任何已知的 HTML 属性传递到 DOM。如果是自定义 React 组件(例如 styled(MyComponent)),styled-components 会将所有 props 传递过去。

此示例显示了 Input 组件的所有 props 如何传递到已安装的 DOM 节点,就像 React 元素一样。

注意 $inputColor 属性没有传递给 DOM,而 typedefaultValue 是传递了的?styled 函数足够智能,可以自动为您过滤非标准属性。

来自 CSS

Styled Components 在组件中如何工作?

如果您熟悉将 CSS 导入组件(例如 CSSModules),那么您习惯于执行类似的操作

import React from 'react';
import styles from './styles.css';


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 (
      <div className={styles.counter}>
        <p className={styles.paragraph}>{this.state.count}</p>
        <button className={styles.button} onClick={this.increment}>
          +
        </button>
        <button className={styles.button} onClick={this.decrement}>
          -
        </button>
      </div>
    );
  }
}

因为 Styled Component 是元素和样式化它的规则的组合,所以我们将像这样编写 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>
    );
  }
}

请注意,我们在 StyledCounter 前添加了 "Styled" 前缀,以便 React 组件 Counter 和 Styled Component StyledCounter 的名称不会冲突,但在 React 开发者工具和 Web 检查器中仍然易于识别。

在 render 方法之外定义 Styled Components

在 render 方法之外定义样式化组件非常重要,否则它将在每次渲染过程中被重新创建。在 render 方法中定义样式化组件会阻止缓存并大幅降低渲染速度,应避免这样做。

以推荐的方式编写样式化组件

const StyledWrapper = styled.div`
  /* ... */
`;


const Wrapper = ({ message }) => {
  return <StyledWrapper>{message}</StyledWrapper>;
};

而不是

const Wrapper = ({ message }) => {
  // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
  const StyledWrapper = styled.div`
    /* ... */
  `;


  return <StyledWrapper>{message}</StyledWrapper>;
};

推荐阅读Talia MarcassaStyled Components: To Use or Not to Use?中写了一篇关于实际使用情况的精彩评论,其中包含许多可靠的实用见解和与替代方案的比较。

伪元素、伪选择器和嵌套

我们使用的预处理器 stylis 支持 scss 样式语法来自动嵌套样式。

通过此预处理,styled-components 支持一些高级选择器模式

  • & 单个与号表示组件的所有实例;它用于应用广泛的覆盖。

    Hello world!
    How ya doing?
    The sun is shining...
    Pretty nice day today.
    Don't you think?
    Splendid.
  • && 双与号表示组件的一个实例;如果您正在执行条件样式覆盖并且不希望样式应用于特定组件的所有实例,这将非常有用。

  • && 单独的双与号具有称为 "优先级提升" 的特殊行为;如果您正在处理混合的 styled-components 和普通 CSS 环境,并且可能存在冲突的样式,这将非常有用。

    I'm blue, da ba dee da ba daa

如果您在没有与号的情况下放置选择器,它们将引用组件的子元素。

附加额外的 props
v2

为了避免不必要的包装器,这些包装器只是将一些属性传递给渲染的组件或元素,您可以使用 .attrs 构造函数。它允许您将其他属性(或 "属性")附加到组件。

这样,您就可以例如将静态属性附加到元素,或将像 activeClassName 这样的第三方属性传递给 React Router 的 Link 组件。此外,您还可以将更多动态属性附加到组件。.attrs 对象还接受函数,这些函数接收组件接收的属性。返回值也将合并到结果属性中。

这里,我们渲染了一个 Input 组件,并为其附加了一些动态和静态属性


如您所见,我们可以在插值中访问新创建的属性,并且 type 属性被传递给元素。

覆盖 .attrs

请注意,当包装样式化组件时,.attrs 从最内层的样式化组件应用到最外层的样式化组件。

这允许每个包装器覆盖嵌套使用的 .attrs,类似于样式表中定义的较晚的 CSS 属性覆盖之前的声明。

Input.attrs 首先应用,然后是 PasswordInput.attrs


这就是为什么 PasswordInput 的类型是 'password',但仍然使用来自 Input$size 属性。

动画

使用 @keyframes 的 CSS 动画不会限定到单个组件,但您仍然不希望它们对所有组件都起作用,以避免名称冲突。这就是为什么我们导出一个 keyframes 助手,它将生成一个唯一的实例,您可以在整个应用程序中使用它

< 💅🏾 >
注意

关键帧不受 react-native 支持。请改用 ReactNative.Animated API

关键帧在使用时会被惰性注入,这就是它们可以代码分割的原因,因此您必须使用 the css helper 用于共享样式片段

const rotate = keyframes``


// ❌ This will throw an error!
const styles = `
  animation: ${rotate} 2s linear infinite;
`


// ✅ This will work as intended
const styles = css`
  animation: ${rotate} 2s linear infinite;
`
注意

这在 v3 及以下版本中有效,因为我们没有对关键帧进行代码分割。如果您要从 v3 升级,请确保所有共享样式片段都使用 css 助手!

React Native

styled-components 可以与 React Native 以相同的方式使用,并使用相同的导入。尝试使用 Snack by Expo 来运行此示例。

import React from 'react'
import styled from 'styled-components/native'


const StyledView = styled.View`
  background-color: papayawhip;
`


const StyledText = styled.Text`
  color: #BF4F74;
`


class MyReactNativeComponent extends React.Component {
  render() {
    return (
      <StyledView>
        <StyledText>Hello World!</StyledText>
      </StyledView>
    )
  }
}

我们还支持更复杂的样式(如 transform),这些样式通常是数组,以及简写(例如 margin),这要归功于 css-to-react-native

注意

请注意,flex 属性的工作方式类似于 CSS 简写,而不是 React Native 中的旧版 flex 属性。设置 flex: 1 除了设置 flexGrow1flexBasis0 外,还会设置 flexShrink1

想象一下您在 React Native 中如何编写此属性,猜测您如何将其转换为 CSS,您可能是对的

const RotatedBox = styled.View`
  transform: rotate(90deg);
  text-shadow-offset: 10px 5px;
  font-variant: small-caps;
  margin: 5px 7px 2px;
`

与 Web 版本的一些区别在于,您不能使用 keyframescreateGlobalStyle 助手,因为 React Native 不支持关键帧或全局样式。如果您使用媒体查询或嵌套 CSS,我们也会发出警告。

注意

在 v2 中,我们支持百分比。为了实现这一点,我们需要为所有简写强制执行单位。如果您要迁移到 v2,可以使用代码修改工具

使用 metro 捆绑器简化使用

如果您更愿意只导入 styled-components 而不是 styled-components/native,您可以添加一个 resolverMainFields 配置,其中包含 "react-native"。这以前在 metro 中默认支持(并且目前在 haul 中确实有效),但似乎在某个时刻被删除了。

请继续阅读下一页

高级