基础

动机(Motivation)

¥Motivation

styled-components 是我们想知道如何增强 CSS 来设计 React 组件系统的结果。通过专注于单个用例,我们成功地优化了开发者的体验以及终端用户的输出。

¥styled-components is the result of wondering how we could enhance CSS for styling React component systems. By focusing on a single use case we managed to optimize the experience for developers as well as the output for end users.

除了改善开发者的体验之外,样式组件还提供:

¥Apart from the improved experience for developers, styled-components provides:

  • 自动关键 CSS:styled-components 完全自动地跟踪页面上渲染的组件并注入它们的样式,而不是其他任何东西。与代码分割相结合,这意味着你的用户加载所需的代码量最少。

    ¥Automatic critical CSS: styled-components keeps track of which components are rendered on a page and injects their styles and nothing else, fully automatically. Combined with code splitting, this means your users load the least amount of code necessary.

  • 没有类名错误:styled-components 为你的样式生成唯一的类名称。你永远不必担心重复、重叠或拼写错误。

    ¥No class name bugs: styled-components generates unique class names for your styles. You never have to worry about duplication, overlap or misspellings.

  • 更轻松地删除 CSS:很难知道代码库中的某个位置是否使用了类名。styled-components 使其变得显而易见,因为每个样式都与特定组件相关联。如果组件未使用(工具可以检测到)并被删除,则其所有样式都会随之删除。

    ¥Easier deletion of CSS: it can be hard to know whether a class name is used somewhere in your codebase. styled-components makes it obvious, as every bit of styling is tied to a specific component. If the component is unused (which tooling can detect) and gets deleted, all its styles get deleted with it.

  • 简单的动态样式:根据组件的 props 或全局主题调整组件的样式既简单又直观,无需手动管理数十个类。

    ¥Simple dynamic styling: adapting the styling of a component based on its props or a global theme is simple and intuitive without having to manually manage dozens of classes.

  • 无痛维护:你永远不必寻找不同的文件来查找影响组件的样式,因此无论你的代码库有多大,维护都是小菜一碟。

    ¥Painless maintenance: you never have to hunt across different files to find the styling affecting your component, so maintenance is a piece of cake no matter how big your codebase is.

  • 自动浏览器前缀:按照当前标准编写 CSS,然后让样式组件处理其余的事情。

    ¥Automatic vendor prefixing: write your CSS to the current standard and let styled-components handle the rest.

你可以获得所有这些好处,同时仍然编写你熟悉和喜爱的 CSS,只需绑定到各个组件即可。

¥You get all of these benefits while still writing the CSS you know and love, just bound to individual components.

安装(Installation)

¥Installation

安装 styled-components 只需一个命令,你就可以开始使用了:

¥Installing styled-components only takes a single command and you're ready to roll:

# with npm
npm install styled-components

# with yarn
yarn add styled-components

如果你使用像 yarn 这样支持 "resolutions" package.json 字段的包管理器,我们还强烈建议你向其中添加一个与主要版本范围相对应的条目。这有助于避免因项目中安装多个版本的样式组件而引起的一系列问题。

¥If you use a package manager like yarn that supports the "resolutions" package.json field, we also highly recommend you add an entry to it as well corresponding to the major version range. This helps avoid an entire class of problems that arise from multiple versions of styled-components being installed in your project.

package.json 年:

¥In package.json:

{
  "resolutions": {
    "styled-components": "^5"
  }
}
Note

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

¥It's highly recommended (but not required) to also use the Babel plugin. It offers many benefits like more legible class names, server-side rendering compatibility, smaller bundles, and more.

Click here to see alternative CDN installation instructions

如果你不使用模块打包器或包管理器,我们还有一个托管在 unpkg CDN 上的全局 ("UMD") 构建。只需将以下 <script> 标记添加到 HTML 文件的底部:

¥If you're not using a module bundler or package manager we also have a global ("UMD") build hosted on the unpkg CDN. Simply add the following <script> tag to the bottom of your HTML file:

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

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

¥Once you've added styled-components you will have access to the global window.styled variable.

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

这种使用风格要求 react CDN 打包react-is CDN 打包包 也位于页面上(在 styled-components 脚本之前)。

¥This style of usage requires the react CDN bundles and the react-is CDN bundle to be on the page as well (before the styled-components script.)

入门(Getting Started)

¥Getting Started

styled-components 利用标记模板字面量来设计组件的样式。

¥styled-components utilises tagged template literals to style your components.

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

¥It removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it.

此示例创建两个简单的组件,一个封装器和一个标题,并附加了一些样式:

¥This example creates two simple components, a wrapper and a title, with some styles attached to it:

Hello World!

这是一个实时编辑器,因此请尝试使用代码来感受使用样式组件的感觉!

¥This is a live editor, so play around with the code to get a feel for what it's like to work with styled-components!

Note

CSS 规则自动添加浏览器前缀,样式组件会为你处理!

¥The CSS rules are automatically vendor prefixed, styled-components takes care of that for you!

样式化组件在底层使用 stylis.js 包作为 css 规则的前缀。

¥Styled components uses stylis.js package under the hood for prefixing the css rules.

有关 stylis.js 中支持的前缀的更多信息,请访问他们的 repository

¥For additional information about the supported prefixes in stylis.js visit their repository.

根据属性进行调整(Adapting based on props)

¥Adapting based on props

你可以将函数 ("interpolations") 传递给样式化组件的模板字面量,以根据其 props 进行调整。

¥You can pass a function ("interpolations") to a styled component's template literal to adapt it based on its props.

该按钮组件有一个改变其颜色的主要状态。当将 $primary 属性设置为 true 时,我们将交换其背景和文本颜色。

¥This button component has a primary state that changes its color. When setting the $primary prop to true, we are swapping out its background and text color.

扩展样式(Extending Styles)

¥Extending Styles

你可能经常想要使用某个组件,但针对单个情况稍微更改一下。现在,你可以传入一个插值函数并根据某些属性更改它们,但是一次覆盖样式需要付出很大的努力。

¥Quite frequently you might want to use a component, but change it slightly for a single case. Now, you could pass in an interpolated function and change them based on some props, but that's quite a lot of effort for overriding the styles once.

要轻松创建继承另一个组件样式的新组件,只需将其封装在 styled() 构造函数中即可。在这里,我们使用上一节中的按钮并创建一个特殊的按钮,并使用一些与颜色相关的样式对其进行扩展:

¥To easily make a new component that inherits the styling of another, just wrap it in the styled() constructor. Here we use the button from the last section and create a special one, extending it with some color-related styling:

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

¥We can see that the new TomatoButton still resembles Button, while we have only added two new rules.

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

¥In some cases you might want to change which tag or component a styled component renders. This is common when building a navigation bar for example, where there are a mix of anchor links and buttons but they should be styled identically.

对于这种情况,我们有一个应急方案。你可以使用 "as" polymorphic prop 动态交换接收你编写的样式的元素:

¥For this situation, we have an escape hatch. You can use the "as" polymorphic prop to dynamically swap out the element that receives the styles you wrote:

这也适用于自定义组件!

¥This works perfectly fine with custom components too!

设计任何组件的样式(Styling any component)

¥Styling any component

styled 方法可以在你自己的所有组件或任何第三方组件上完美运行,只要它们将传递的 className 属性附加到 DOM 元素即可。

¥The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.

Note

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

¥If you are using react-native keep in mind to use style instead of className.

Note

你还可以将标签名称传递到 styled() 工厂调用中,如下所示:styled("div")。事实上,styled.tagname 助手只是做同样事情的别名。

¥You can also pass tag names into the styled() factory call, like so: styled("div"). In fact, the styled.tagname helpers are just aliases that do the same.

传递的属性(Passed props)

¥Passed props

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

¥If the styled target is a simple element (e.g. styled.div), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)), styled-components passes through all props.

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

¥This example shows how all props of the Input component are passed on to the DOM node that is mounted, as with React elements.

请注意,$inputColor 属性没有传递到 DOM,但 typedefaultValue 却传递到了 DOM?styled 功能足够智能,可以自动为你过滤非标准属性。

¥Note how the $inputColor prop is not passed to the DOM, but type and defaultValue are? The styled function is smart enough to filter non-standard attributes automatically for you.

来自 CSS(Coming from CSS)

¥Coming from CSS

样式化组件如何在组件内工作?(How do Styled Components work within a component?)

¥How do Styled Components work within a component?

如果你熟悉将 CSS 导入组件(例如 CSSModules),你将习惯于执行以下操作:

¥If you're familiar with importing CSS into your components (e.g. like CSSModules) you'll be used to doing something like this:

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>
    );
  }
}

因为样式组件是元素和样式规则的组合,所以我们将 Counter 写成这样:

¥Because a Styled Component is the combination of the element and the rules that style it, we'd write Counter like this:

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 中添加了 "风格" 前缀,以便 React 组件 Counter 和样式组件 StyledCounter 不会发生名称冲突,但在 React Developer Tools 和 Web Inspector 中仍然可以轻松识别。

¥Note that we added a "Styled" prefix to StyledCounter so that the React component Counter and the Styled Component StyledCounter don't clash names but remain easily identifiable in the React Developer Tools and Web Inspector.

在渲染方法之外定义样式组件(Define Styled Components outside of the render method)

¥Define Styled Components outside of the render method

在渲染方法之外定义样式组件非常重要,否则它将在每个渲染通道上重新创建。在渲染方法中定义样式组件会阻碍缓存并大大降低渲染速度,应该避免。

¥It is important to define your styled components outside of the render method, otherwise it will be recreated on every single render pass. Defining a styled component within the render method will thwart caching and drastically slow down rendering speed, and should be avoided.

按照推荐的方式编写样式组件:

¥Write your styled components the recommended way:

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


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

代替:

¥Instead of:

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


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

推荐阅读:Talia Marcassa样式组件:使用还是不使用? 中写了一篇关于实际使用情况的精彩评论,其中包含许多扎实的实用见解以及与替代方案的比较

¥Recommended reading: Talia Marcassa wrote a great review of real-world usage, featuring lots of solid practical insights and comparisons with alternatives, in Styled Components: To Use or Not to Use?

伪元素、伪选择器和嵌套(Pseudoelements, pseudoselectors, and nesting)

¥Pseudoelements, pseudoselectors, and nesting

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

¥The preprocessor we use, stylis, supports scss-like syntax for automatically nesting styles.

通过这种预处理,样式组件支持一些高级选择器模式:

¥Through this preprocessing, styled-components supports some advanced selector patterns:

  • & 单个 & 符号指的是组件的所有实例;它用于应用广泛的覆盖:

    ¥& a single ampersand refers to all instances of the component; it is used for applying broad overrides:

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

    ¥&& a double ampersand refers to an instance of the component; this is useful if you're doing conditional styling overrides and don't want a style to apply to all instances of a particular component:

  • && 单独的双 & 符号具有称为 "优先级提升" 的特殊行为;如果你正在处理可能存在样式冲突的混合样式组件和普通 CSS 环境,这可能会很有用:

    ¥&& a double ampersand alone has a special behavior called a "precedence boost"; this can be useful if you are dealing with a mixed styled-components and vanilla CSS environment where there might be conflicting styles:

    I'm blue, da ba dee da ba daa

如果你放置的选择器不带 & 符号,它们将引用组件的子组件。

¥If you put selectors in without the ampersand, they will refer to children of the component.

附加额外属性(Attaching additional props)

¥Attaching additional props

| v2

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

¥To avoid unnecessary wrappers that just pass on some props to the rendered component or element, you can use the .attrs constructor. It allows you to attach additional props (or "attributes") to a component.

通过这种方式,你可以将静态属性附加到元素或将第三方属性(如 activeClassName)传递给 React Router 的 Link 组件。此外,你还可以将更多动态属性附加到组件。.attrs 对象还采用函数来接收组件接收的 props。返回值也将合并到生成的 props 中。

¥This way you can for example attach static props to an element or pass a third-party prop like activeClassName to React Router's Link component. Furthermore, you can also attach more dynamic props to a component. The .attrs object also takes functions, that receive the props that the component receives. The return value will be merged into the resulting props as well.

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

¥Here we render an Input component and attach some dynamic and static attributes to it:


正如你所看到的,我们可以在插值中访问新创建的 props,并且 type 属性会向下传递给元素。

¥As you can see, we get access to our newly created props in the interpolations, and the type attribute is passed down to the element.

覆盖 .attrs(Overriding .attrs)

¥Overriding .attrs

请注意,在封装样式组件时,.attrs 将从最里面的样式组件应用到最外面的样式组件。

¥Notice that when wrapping styled components, .attrs are applied from the innermost styled component to the outermost styled component.

这允许每个封装器覆盖 .attrs 的嵌套使用,类似于样式表中稍后定义的 CSS 属性如何覆盖先前的声明。

¥This allows each wrapper to override nested uses of .attrs, similarly to how CSS properties defined later in a stylesheet override previous declarations.

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

¥Input's .attrs are applied first, and then PasswordInput's .attrs:


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

¥This is why PasswordInput is of a 'password' type, but still uses the $size attribute from Input.

动画(Animations)

¥Animations

带有 @keyframes 的 CSS 动画不限于单个组件,但你仍然不希望它们是全局的以避免名称冲突。这就是我们导出 keyframes 辅助程序的原因,它将生成一个可以在整个应用中使用的唯一实例:

¥CSS animations with @keyframes aren't scoped to a single component but you still don't want them to be global to avoid name collisions. This is why we export a keyframes helper which will generate a unique instance that you can use throughout your app:

< 💅🏾 >
Note

react-native 不支持关键帧。相反,请使用 ReactNative.Animated API

¥Keyframes are not supported by react-native. Instead, use the ReactNative.Animated API.

关键帧在使用时会延迟注入,这就是它们可以进行代码分割的方式,因此你必须使用 css 帮手 来共享样式片段:

¥Keyframes are lazily injected when they're used, which is how they can be code-split, so you have to use the css helper for shared style fragments:

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;
`
Note

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

¥This used to work in v3 and below where we didn't code-split keyframes. If you're upgrading from v3, make sure that all your shared style fragments are using the css helper!

React Native(React Native)

styled-components 可以以相同的方式和相同的导入与 React Native 一起使用。使用 Expo 小样 尝试此示例。

¥styled-components can be used with React Native in the same way and with the same import. Try this example with 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

¥We also support more complex styles (like transform), which would normally be an array, and shorthands (e.g. for margin) thanks to css-to-react-native!

Note

请注意,flex 属性的工作方式类似于 CSS 速记,而不是 React Native 中旧版的 flex 属性。除了将 flexGrow 设置为 1flexBasis0 之外,设置 flex: 1 还会将 flexShrink 设置为 1

¥Note that the flex property works like CSS shorthand, and not the legacy flex property in React Native. Setting flex: 1 sets flexShrink to 1 in addition to setting flexGrow to 1 and flexBasis to 0.

想象一下你如何在 React Native 中编写该属性,猜猜你如何将其传输到 CSS,你可能是对的:

¥Imagine how you'd write the property in React Native, guess how you'd transfer it to CSS, and you're probably right:

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

与网络版本的一些区别是,你不能使用 keyframescreateGlobalStyle 辅助程序,因为 React Native 不支持关键帧或全局样式。如果你使用媒体查询或嵌套 CSS,我们还会向你触发警告。

¥Some of the differences to the web-version are, that you cannot use the keyframes and createGlobalStyle helpers since React Native doesn't support keyframes or global styles. We will also warn you if you use media queries or nest your CSS.

Note

在 v2 中我们支持百分比。为了实现这一点,我们需要强制所有速记单位。如果你要迁移到 v2,则 有可用的代码模式

¥In v2 we support percentages. To make this possible we need to enforce units for all shorthands. If you're migrating to v2, a codemod is available.

Metro 打包器的使用更简单(Simpler usage with the metro bundler)

¥Simpler usage with the metro bundler

如果你希望仅导入 styled-components 而不是 styled-components/native,则可以添加包含 "react-native"resolverMainFields 配置。这曾经在 Metro 中默认受支持(目前在 haul 中有效),但似乎已在某个时候被删除。

¥If you'd prefer to just import styled-components instead of styled-components/native, you can add a resolverMainFields configuration that includes "react-native". This used to be supported in metro by default (and currently does work in haul) but appears to have been removed at some point.

继续下一页

高级