常见问题

我需要做什么才能迁移到 v6?(What do I need to do to migrate to v6?)

¥What do I need to do to migrate to v6?

首先,我们首先将必要的软件包更新到最新版本。

¥First, let's start by updating the necessary packages to their latest versions.

如果你使用 NPM:

¥If you're using NPM:

npm install styled-components@^6.0.0 stylis@^4.0.0
npm uninstall @types/styled-components

如果你使用 Yarn:

¥If you're using Yarn:

yarn add styled-components@^6.0.0 stylis@^4.0.0
yarn remove @types/styled-components

由于样式组件现在提供了自己的类型,因此不再需要社区类型。

¥As styled-components now provides its own types, there's no longer a need for community ones.

TypeScript(TypeScript)

对于 TypeScript 爱好者来说好消息 - 样式组件现在是用 TypeScript 原生编写的!即使你以前没有使用过 TypeScript,也建议你使用 TypeScript 来提高项目的可靠性,因为当你使用未知的 prop 或 prop 的值与预期不同时,它可以向你触发警报。

¥Good news for TypeScript enthusiasts – styled-components is now natively written in TypeScript! Even if you haven't used TypeScript before, it's recommended for improving the reliability of your project as it can alert you when you're using an unknown prop or if a prop has a different value than expected.

但是,如果你在项目中不使用 TypeScript,请不要担心!VS Code 等 IDE 仍会在你编写代码时获取类型并提供提示。

¥However, if you don't use TypeScript in your project, don't worry! IDEs like VS Code will still pick up the types and provide hints as you write your code.

默认情况下不再提供 shouldForwardProp(shouldForwardProp is no longer provided by default)

¥shouldForwardProp is no longer provided by default

如果尚未将样式迁移为使用 瞬态属性 ($prefix),你可能会注意到 React 关于样式属性在 v6 中传递到 DOM 的警告。要恢复 v5 行为,请使用 StyleSheetManager

¥If haven't migrated your styling to use transient props ($prefix), you might notice React warnings about styling props getting through to the DOM in v6. To restore the v5 behavior, use StyleSheetManager:

import isPropValid from '@emotion/is-prop-valid';
import { StyleSheetManager } from 'styled-components';


function MyApp() {
    return (
        <StyleSheetManager shouldForwardProp={shouldForwardProp}>
            {/* other providers or your application's JSX */}
        </StyleSheetManager>
    )
}


// This implements the default behavior from styled-components v5
function shouldForwardProp(propName, target) {
    if (typeof target === "string") {
        // For HTML elements, forward the prop if it is a valid HTML attribute
        return isPropValid(propName);
    }
    // For other elements, forward all props
    return true;
}

默认情况下省略浏览器前缀(Vendor prefixes are omitted by default)

¥Vendor prefixes are omitted by default

随着网络和浏览器到 2023 年已经显着成熟,浏览器前缀通常是不必要的。因此,对于 v6 版本,我们决定默认省略自动前缀,以减少传递到页面的 CSS 量。如果你更喜欢 v5 行为,可以通过 StyleSheetManager 恢复它:

¥As the web and browsers have matured significantly by 2023, vendor prefixing is often unnecessary. Therefore, for the v6 release, we've decided to omit automatic prefixing by default to reduce the amount of CSS delivered to a page. If you prefer the v5 behavior, you can restore it via StyleSheetManager:

import { StyleSheetManager } from 'styled-components';


function MyApp() {
    return (
        <StyleSheetManager enableVendorPrefixes>
            {/* other providers or your application's JSX */}
        </StyleSheetManager>
    )
}

为了适应这一变化,原来的 disableVendorPrefixes 属性被倒置为 enableVendorPrefixes;如果你设置了 disableVendorPrefixes,你现在可以将其删除,因为它是新的默认值。

¥To accommodate this change, the original disableVendorPrefixes prop was inverted to enableVendorPrefixes; if you have disableVendorPrefixes set, you can now remove it as it's the new default.

更新 stylis 插件(Update stylis plugins)

¥Update stylis plugins

styled-components v6 使用较新的 stylis v4;如果你提供 stylisPluginsStyleSheetManager,请确保插件是最新的。例如,stylis-plugin-rtl 发布了新版本来支持更新的 stylis。

¥styled-components v6 uses the newer stylis v4; if you are providing stylisPlugins to StyleSheetManager, ensure the plugins are up-to-date. For instance, stylis-plugin-rtl released a new version to support the updated stylis.

嵌套语法处理(Nested syntax handling)

¥Nested syntax handling

使用 stylis v4 升级 处理嵌套选择器的方式发生了变化,现在可以正确反映浏览器行为。具体来说,不以 & 开头的伪选择器(例如 :before)将不再隐式添加 & 符号。

¥With the stylis v4 upgrade came a change to how nested selectors are handled which now correctly mirrors browser behavior. Specifically, pseudoselectors (e.g. :before) that do not start with & will not have the ampersand implicitly added anymore.

v5 行为

¥v5 behavior

styled.div`
  :hover { color: red; }
`
// .[classname]:hover { color: red; }


styled.div`
  &:hover { color: red; }
`
// .[classname]:hover { color: red; }

v6 行为

¥v6 behavior

styled.div`
  :hover { color: red; }
`
// .[classname] :hover { color: red; } (equivalent to .[classname] *:hover)


styled.div`
  &:hover { color: red; }
`
// .[classname]:hover { color: red; }

瞬态 $as$forwardedAs 属性已被丢弃(Transient $as and $forwardedAs props have been dropped)

¥Transient $as and $forwardedAs props have been dropped

为了减少应用顺序的混乱,我们删除了瞬态 $as$forwardedAs 属性。请改用常规的 asforwardedAs 属性。

¥To reduce confusion around application order, we've dropped the transient $as and $forwardedAs props. Please use the regular as and forwardedAs props instead.

放弃旧版 withComponent() API(Dropped legacy withComponent() API)

¥Dropped legacy withComponent() API

这种变化已经发生很长时间了。不再支持 withComponent API,因此请改用 as 属性。你可以在定义时通过 attrs 或在运行时指定 as

¥This change has been a long time coming. The withComponent API is no longer supported, so please use the as prop instead. You can specify as at definition time via attrs or at runtime:

import styled from 'styled-components';


const Button = styled.button`
    background: blue;
    color: white;
`;


const ButtonLink = styled(Button).attrs({ as: 'a' })``;


// These are equivalent, but `ButtonLink` allows for attaching further styling if desired.
<Button as="a" href="https://styled-components.nodejs.cn">
<ButtonLink href="https://styled-components.nodejs.cn">

最小 Node 支持提升至 v16+(Minimum Node support raised to v16+)

¥Minimum Node support raised to v16+

与 Node 的 维护计划 一致,我们现在支持 v16 作为仍在接收安全补丁的最旧的运行时。

¥In line with the maintenance schedule for Node, we now support v16 as the oldest runtime that's still receiving security patches.

我需要做什么才能迁移到 v5?(What do I need to do to migrate to v5?)

¥What do I need to do to migrate to v5?

准备好了吗?

¥Ready for this?

npm install styled-components@^5.0.0 react@^16.8 react-dom@^16.8 react-is@^16.8

如果你使用 React Native,则至少需要 v0.59(第一个支持 hooks 的版本。)

¥If you're using React Native, you'll need at least v0.59 (the first version to support hooks.)

就是这样。💥

¥That's it. 💥


styled-components v5 没有引入任何重大的公共 API 更改,并添加了以下内容:

¥styled-components v5 does not introduce any breaking public API changes, and adds the following:

  • 完全重写核心样式表引擎,并针对性能进行了调整

    ¥Total rewrite of the core stylesheet engine, tuned for performance

  • 新的基于钩子的组件模型

    ¥New hooks-based component model

  • StyleSheetManager 有新属性:

    ¥StyleSheetManager has new props:

    • disableCSSOMInjection

    • disableVendorPrefixes

    • stylisPlugins

Note

注意:v4 中已弃用的子函数对象形式 .attrs({ prop: props => {} }) 语法已在 v5 中删除。使用函数形式的 attrs 而不是 .attrs(props => ({}))(你应该已经看到控制台警告以提前进行此更新。)

¥Note: The subfunction object-form .attrs({ prop: props => {} }) syntax that was deprecated in v4 is removed in v5. Use function-form attrs instead .attrs(props => ({})) (you should have been seeing console warnings to make this update ahead of time.)

查看 官方公告帖 了解更多信息并了解 v5 的内容!

¥Check out the official announcement post for more information and to learn about what went into v5!

对于 jest 用户(For jest users)

¥For jest users

更新到 jest-styled-components v7:

¥Update to jest-styled-components v7:

npm install jest-styled-components@^7.0.0

关于 css @importcreateGlobalStyle 的注意事项(Note regarding css @import and createGlobalStyle)

¥Note regarding css @import and createGlobalStyle

目前,我们不建议在 cGS 中使用 @import,因为浏览器如何通过 CSSOM API 处理 @import 存在一些问题。相反,最好将它们放置在典型 <style> 标记内的核心 index.html 文件(生成的或静态的)中。

¥At this time we do not recommend using @import within cGS due to some issues with how browsers process @import via the CSSOM APIs. Instead it's best to place these in your core index.html file (generated or static) within a typical <style> tag.

我需要做什么才能迁移到 v4?(What do I need to do to migrate to v4?)

¥What do I need to do to migrate to v4?

这是一个相当大的版本,在底层和 API 级别都有很多变化。随着测试版的进展,我们将尝试发布代码模组以使以下项目变得更简单。另外,如果你发现以下步骤有任何问题,请 留下建设性反馈

¥This is a pretty big release with lots of changes both under the hood and at the API level. As the beta progresses, we will try to release codemods to make the items below simpler. Also, if you find any issues with the steps below, please leave constructive feedback!

  1. 升级到最新的样式组件:

    ¥Upgrade to the latest styled-components:

npm install styled-components@^4.0.0
  1. 确保你的应用使用 react >= 16.3;在内部,如果你想尝试使用 polyfill 来支持较旧的 React 版本,我们正在使用新的 React.forwardRef API 和新的上下文 API

    ¥Make sure your application is using react >= 16.3; internally we are using the new React.forwardRef API and new context APIs if you wish to try and polyfill for older React version support

npm install react@^16.3 react-dom@^16.3
Note

如果你使用的是 enzyme 或其他依赖(例如 react-test-renderer),并且你来自旧版本的 react,则可能需要完成更多相关升级。

¥If you are using enzyme or other dependencies like react-test-renderer, there may be more related upgrades to complete if you are coming from an old version of react.

  1. 如果你使用的是 .extend API,请将组件切换为使用 styled(StyledComponent)

    ¥If you are using the .extend API, switch your components to use styled(StyledComponent) instead.

代码模式可用 可以加快这一过程。

¥A codemod is available to expedite this.

🚫

import styled from 'styled-components'


const Component = styled.div`
  background: blue;
  color: red;
`


const ExtendedComponent = Component.extend`
  color: green;
`

import styled from 'styled-components'


const Component = styled.div`
  background: blue;
  color: red;
`


const ExtendedComponent = styled(Component)`
  color: green;
`

有关更多示例,请参阅 "扩展样式" 文档

¥See the "extending styles" documentation for more examples.

  1. 如果你使用 injectGlobal API 将全局样式添加到页面,请改用新的 createGlobalStyle 辅助组件

    ¥If you were using the injectGlobal API to add global styles to your page, use the new createGlobalStyle helper component instead.

代码模式可用 可以加快这一过程。

¥A codemod is available to expedite this.

🚫

import { injectGlobal } from 'styled-components'


injectGlobal`
  body {
    color: red;
  }
`

import { createGlobalStyle } from "styled-components"


const GlobalStyle = createGlobalStyle`
  body {
    color: red;
  }
`


// later in your app's render method
<React.Fragment>
  <Navigation />
  <OtherImportantTopLevelComponentStuff />
  <GlobalStyle />
</React.Fragment>

请参阅 createGlobalStyle 的文档,了解你可以用它做的所有很酷的事情,而这些事情以前用 injectGlobal 是不可能的!

¥See the documentation for createGlobalStyle to see all the cool stuff you can do with it that wasn't possible before with injectGlobal!

  1. 如果你使用的是 innerRef 属性,请将其更改为普通的 ref

    ¥If you were using the innerRef prop, change it to a normal ref.

🚫

const Component = styled.div`
  background: blue;
  color: red;
`


// later in your render method
<Component innerRef={element => { this.myElement = element }}>Something something</Component>

const Component = styled.div`
  background: blue;
  color: red;
`


// later in your render method
<Component ref={element => { this.myElement = element }}>Something something</Component>
  1. 如果你在没有 css 辅助程序的情况下在部分中使用 keyframes 组件,那么你现在需要使用该辅助程序。一般来说,在编写要插入到样式化组件中的样式部分时,请始终使用 css 助手。

    ¥If you're using the keyframes component in a partial without the css helper, you'll need to use the helper now. In general, always use the css helper when composing styling partials to be interpolated into a styled component.

🚫

import styled, { keyframes } from 'styled-components'


const animation = keyframes`
  0% {
    opacity: 0;
  }


  100 {
    opacity: 1;
  }
`


const animationRule = `
  ${animation} 1s infinite alternate
`


const Component = styled.div`
  animation: ${animationRule};
`

import styled, { css, keyframes } from 'styled-components'


const animation = keyframes`
  0% {
    opacity: 0;
  }


  100 {
    opacity: 1;
  }
`


const animationRule = css`
  ${animation} 1s infinite alternate;
`


const Component = styled.div`
  animation: ${animationRule};
`
  1. 如果你使用 attrs({}) 并且传递给它的某些属性是函数,建议切换到新的 attrs(props => ({})) 语法,以获得更简单、更强大的组合。

    ¥If you're using attrs({}) and some of the attributes you pass to it is a Function, it's recommended to switch to the new attrs(props => ({})) syntax instead for easier and more powerful composition.

🚫

import styled from 'styled-components'


const Input = styled.input.attrs({
  type: props => props.inputType,
})`
  background: blue;
  color: red;
`

import styled from 'styled-components'


const Input = styled.input.attrs(props => ({
  type: props.inputType,
}))`
  background: blue;
  color: red;
`
  1. 如果你使用 TypeScript,则类型现在位于 DefinelyTyped 中:

    ¥If you're using TypeScript, the typings are now located in DefinitelyTyped:

npm install @types/styled-components

就是这样!除了迁移之外,我们还强烈建议你阅读新的 "as" prop,它将在未来取代 withComponent API

¥That's it! Aside from migrating, we also highly recommend reading up on the new "as" prop which is intended to replace the withComponent API in the future.

我可以嵌套规则吗?(Can I nest rules?)

¥Can I nest rules?

是的:嵌套是特意从 Sass 移植的一个功能。谨慎使用它是通过减少为每个元素创建显式类的需要来减轻代码负担的好方法。

¥Yes: nesting is a feature intentionally ported from Sass. Used sparingly it's a great way to lighten your code by reducing the need to create explicit classes for every element.

父组件还可以使用它来定义受影响子组件不关心的上下文约束:

¥It can also be used by parent components to define contextual constraints that aren't properly a concern of the affected children:

First
Second
Third
First
Second
Third

共同定位媒体查询也非常方便,因为我们可以一目了然地看到组件在任何分辨率下将如何响应。

¥It's also incredibly convenient to co-locate media queries, since we can see at a glance exactly how the component will respond at any resolution.

Hello world!

我可以使用 CSS 框架吗?(Can I use CSS frameworks?)

¥Can I use CSS frameworks?

将现有 CSS 框架与样式组件集成非常简单!你可以将其现有的类名称与你的组件一起使用。

¥Integrating an existing CSS framework with styled-components is really easy! You can use its existing class names alongside your components.

例如,假设你有一个现有的应用,其中有两个你想要再次使用的类:.small.big。如果你希望该类始终附加到组件,则应该使用 attrs 方法 来附加它。如果你只想在某些情况下附加它,你可以像往常一样使用 className 属性!

¥For example, imagine you have an existing app with two classes you want to use again: .small and .big. If you want the class to always be attached to the component, you should use the attrs method to attach it. If you want to attach it only in some cases you can use the className props like you always have!

如果框架有一堆需要包含在页面上的原始全局 CSS,你可以使用 createGlobalStyle API.CSS 添加它。这对于 CSS 重置之类的事情也很有用。

¥If the framework has a bunch of raw global CSS that needs to be included on the page, you can add it using the createGlobalStyle API. This is also useful for things like CSS resets.

Note

请注意,对于 styled-components v3 及更低版本,之前的全局样式 API 是 injectGlobal

¥Note that for styled-components v3 and below, the previous API for global styles was injectGlobal.

如何以更高的特异性覆盖样式?(How can I override styles with higher specificity?)

¥How can I override styles with higher specificity?

覆盖具有高特异性的样式的方法是简单地增加你自己的样式的特异性。这可以使用 !important 来完成,但这很容易出错,而且通常不是一个好主意。

¥The way to override styles with a high specificity is to simply increase the specificity of your own styles. This could be done using !important, but that's error prone and generally not a good idea.

我们推荐以下技术:

¥We recommend the following technique:

const MyStyledComponent = styled(AlreadyStyledComponent)`
  &&& {
    color: #BF4F74;
    font-weight: bold;
  }
`

每个 & 都会被生成的类替换,因此注入的 CSS 看起来像这样:

¥Each & gets replaced with the generated class, so the injected CSS then looks like this:

.MyStyledComponent-asdf123.MyStyledComponent-asdf123.MyStyledComponent-asdf123 {
  color: #BF4F74;
  font-weight: bold;
}

重复的类将特异性提高到足以覆盖源顺序,而编写起来又不会很乏味!

¥The repeated class bumps the specificity high enough to override the source order without being very tedious to write!

如何覆盖内联样式?(How can I override inline styles?)

¥How can I override inline styles?

内联样式始终优先于外部 CSS,因此你不能通过简单地增加特异性来覆盖它。

¥Inline styles will always take precedence over external CSS, so you cannot override it by simply increasing specificity.

然而,有一个巧妙的技巧,即将样式 element-attr CSS 选择器与 !important 结合使用:

¥There is a neat trick however, which is to use the style element-attr CSS Selector in conjunction with !important:

const MyStyledComponent = styled(InlineStyledComponent)`
  &[style] {
    font-size: 12px !important;
    color: blue !important;
  }
`

为什么我的 DOM 节点有两个类?(Why do my DOM nodes have two classes?)

¥Why do my DOM nodes have two classes?

每个节点实际上有两个类连接到它:一个是每个组件静态的,这意味着样式化组件的每个元素都有这个类。它没有任何附加的风格。相反,它用于快速识别 DOM 对象属于哪个样式组件或在 DevTools 中进行细微更改。它也用于 组件选择器。静态类可能看起来像这样:.sc-fVOeaW

¥Each node actually has two classes connected to it: one is static per component, meaning each element of a styled component has this class. It hasn't any style attached to it. Instead, it's used to quickly identify which styled component a DOM objects belongs to or to make minor changes in the DevTools. It's also used for component selectors. The static class probably will look something like: .sc-fVOeaW.

另一个是动态的,这意味着根据插值的结果,具有不同属性的样式组件的每个元素都会有所不同。它可能看起来像 .fVOeaW(注意缺少 "sc" 前缀。)

¥The other is dynamic, meaning it will be different for every element of your styled component with different props, based on what the interpolations result in. It will probably look like .fVOeaW (note the lack of "sc" prefix.)

例如,样式化组件 <Button /> 每次都会使用相同的静态类进行渲染。如果使用插值来更改样式,例如 <Button secondary />,则动态类将是不同的,而静态类将保持不变。

¥For example, the styled component <Button /> would render with the same static class every time. If the styles are changed using interpolations, like <Button secondary />, then the dynamic class will be a different one, while the static class would remain the same.

什么时候使用属性?(When to use attrs?)

¥When to use attrs?

你可以使用 attrs 将属性传递给样式组件,但这样做并不总是明智的。

¥You can pass in attributes to styled components using attrs, but it is not always sensible to do so.

经验法则是,当你希望样式化组件的每个实例都具有该属性时,请使用 attrs;当每个实例都需要不同的属性时,请直接使用 传递属性

¥The rule of thumb is to use attrs when you want every instance of a styled component to have that prop, and pass props directly when every instance needs a different one:

const PasswordInput = styled.input.attrs(props => ({
  // Every <PasswordInput /> should be type="password"
  type: "password"
}))``


// This specific one is hidden, so let's set aria-hidden
<PasswordInput aria-hidden="true" />

对于可以根据另一个属性的 "mode" 推断的属性也是如此。在这种情况下,你可以将 attrs 上的属性设置为根据其他 props 计算该 prop 的函数。

¥The same goes for props that can be inferred based on the "mode" of another prop. In this case you can set a property on attrs to a function that computes that prop based on other props.

我是一名库作者。我应该将 styled-components 与我的库打包在一起吗?(I am a library author. Should I bundle styled-components with my library?)

¥I am a library author. Should I bundle styled-components with my library?

如果你是库作者,我们建议你不要将 styled-components 模块与你的库打包和发布。要实现此目标,你需要执行两个步骤:

¥If you are a library author, we recommend that you should not bundle and ship styled-components module with your library. There are two steps that you need to do to achieve this:

  • 在包依赖中将 styled-components 标记为外部

    ¥Marking styled-components as external in your package dependencies

  • 从你的库包中删除 styled-components

    ¥Removing styled-components from your library bundle

在包依赖中将 styled-components 标记为外部(Marking styled-components as external in your package dependencies)

¥Marking styled-components as external in your package dependencies

为此,你需要将其从 dependencies 移动到 devDependencies 并将其包含在 package.json 文件的 peerDependencies 列表中:

¥To do this, you will need to move it from dependencies to devDependencies and include it in the peerDependencies list in your package.json file:

{
-   "dependencies" : {
+   "devDependencies" : {
      "styled-components": "^3.4.9"
    },
+   "peerDependencies" : {
+     "styled-components": ">= 3"
+   }
  }

styled-components 移动到 devDependencies 将保证它不会与你的库一起安装(安装库时 npm installyarn add 将忽略 devDependencies)。

¥Moving styled-components to devDependencies will guarantee that it wouldn't be installed along with your library (npm install or yarn add will ignore devDependencies when a library is installed).

styled-components 添加到 peerDependencies 将向你的库使用者触发信号,表明库中不包含 styled-components,他们需要自行安装。

¥Adding styled-components to peerDependencies will signal your library consumers that styled-components is not included with the library and they need to install it themselves.

另请注意,在 peerDependencies 部分中,版本字符串已变为更宽松的 >= 3。这允许样式组件的未来版本自动工作,如果最终添加了重大更改,你可以通过对库进行补丁更新来简单地缩小范围。

¥Also, note that in the peerDependencies section the version string has been made a more permissive >= 3. This allows future versions of styled-components to work automatically and you can simply narrow the range with a patch update to your library if a breaking change is eventually added.

从你的库包中删除 styled-components(Removing styled-components from your library bundle)

¥Removing styled-components from your library bundle

如果你在发货前打包了库,请确保没有将 styled-components 与它打包在一起。以下是如何使用一些流行的模块打包工具执行此操作的一些示例:

¥If you are bundling your library before shipping it, make sure that you are not bundling styled-components along with it. Here are some examples of how to do this with some popular module bundling tools:

使用 Microbundle(With Microbundle)

¥With Microbundle

如果你使用的是 微束,它将自动处理此步骤。Microbundle 将 peerDependencies 列表中的每个依赖视为外部依赖,并将其从构建中排除。

¥If you are using Microbundle, it will handle this step automatically. Microbundle treats every dependency in the peerDependencies list as external and excludes it from the build for you.

使用 Rollup.js(With Rollup.js)

¥With Rollup.js

如果你使用 Rollup.js,则应在配置中提供 external 选项:

¥If you are using Rollup.js, you should provide an external option in your config:

export default {
    entry: "my-awesome-library.js",
+   external: [
+     "styled-components"
+   ]
  }

另一种方法是使用 rollup-plugin-peer-deps-external 插件,它会自动将 peerDependencies 添加到 external 选项数组中。

¥Another approach is to use the rollup-plugin-peer-deps-external plugin which will automatically add the peerDependencies in the external option array for you.

+ import peerDepsExternal from 'rollup-plugin-peer-deps-external';

  export default {
    entry: "my-awesome-library.js",
+   plugins: [
+    // Preferably set as first plugin.
+    peerDepsExternal(),
+   ]
  }

使用 Webpack(With Webpack)

¥With Webpack

如果你使用 Webpack,则应在配置中提供 externals 选项:

¥If you are using Webpack, you should provide an externals option in your config:

modules.export = {
    entry: "my-awesome-library.js",
+   externals: {
+     "styled-components": {
+       commonjs: "styled-components",
+       commonjs2: "styled-components",
+       amd: "styled-components",
+     },
+   },
  }
Note

你可以在 Webpack 文档的 "创作库" 部分找到有关如何将库与 Webpack 打包在一起的更多有用信息。

¥You can find more useful information on how to bundle a library with Webpack at "Authoring Libraries" section of Webpack documentation.

为什么我会收到有关页面上多个模块实例的警告?(Why am I getting a warning about several instances of module on the page?)

¥Why am I getting a warning about several instances of module on the page?

如果你在控制台中看到如下所示的警告消息,则页面上可能已初始化了多个 styled-components 实例。

¥If you are seeing a warning message in the console like the one below, you probably have several instances of styled-components initialized on the page.

It looks like there are several instances of "styled-components" initialized in this application. This may cause dynamic styles not rendering properly, errors happening during rehydration process and makes you application bigger without a good reason.


If you are using a building tool like webpack, consider checking your bundle for duplication of the "styled-components" module.

如果你使用服务器端渲染,这可能会导致动态样式无法正常工作,甚至在补水期间出现错误。

¥This may cause dynamic styles not working properly or even errors during rehydration if you are using server-side rendering.

可能的原因(Possible reasons)

¥Possible reasons

发生这种情况有几个常见原因:

¥There are several common reasons for this to happen:

  • 你有多个使用 styled-components 的应用在同一页面上运行(例如,webpack 中的多个入口点加载在同一页面上)

    ¥You have several applications that are using styled-components running on the same page (e.g., several entry points in webpack are loaded on the same page)

  • 你的依赖中有另一个 styled-components

    ¥You have another styled-components library somewhere in your dependencies

  • 你的项目有一个 monorepo 结构(例如 lerna、yarn 工作区),并且 styled-components 模块是多个包的依赖(这个包或多或少与前一个包相同)

    ¥You have a monorepo structure for your project (e.g, lerna, yarn workspaces) and styled-components module is a dependency in more than one package (this one is more or less the same as the previous one)

在一个页面上运行多个应用(Running multiple applications on one page)

¥Running multiple applications on one page

如果你在一个页面上运行多个应用,请考虑为所有这些应用使用一个 styled-components 模块。如果你使用 webpack,则可以使用 CommonsChunkPlugin 创建 显式库块,其中将包含 styled-components 模块:

¥If you have several applications running on one page, consider using one styled-components module for all of them. If you are using webpack, you can use CommonsChunkPlugin to create an explicit vendor chunk, that will contain the styled-components module:

module.exports = {
    entry: {
+     vendor: ["styled-components"],
      app1: "./src/app.1.js",
      app2: "./src/app.2.js",
    },
    plugins: [
+     new webpack.optimize.CommonsChunkPlugin({
+       name: "vendor",
+       minChunks: Infinity,
+     }),
    ]
  }

node_modules 中的重复模块(Duplicated module in node_modules)

¥Duplicated module in node_modules

如果你认为问题出在依赖中某处重复的 styled-components 模块中,有多种方法可以检查这一点。你可以在应用文件夹中使用 npm ls styled-componentsyarn list --pattern styled-componentsfind -L ./node_modules | grep /styled-components/package.json 命令。

¥If you think that the issue is in duplicated styled-components module somewhere in your dependencies, there are several ways to check this. You can use npm ls styled-components, yarn list --pattern styled-components or find -L ./node_modules | grep /styled-components/package.json commands in your application folder.

如果这些命令均未识别出重复项,请尝试分析你的打包包中是否存在 styled-components 的多个实例。你可以只检查你的打包包源,或者使用 source-map-explorerwebpack-bundle-analyzer 等工具。

¥If none of these commands identified the duplication, try analyzing your bundle for multiple instances of styled-components. You can just check your bundle source, or use a tool like source-map-explorer or webpack-bundle-analyzer.

如果你发现重复是你遇到的问题,你可以尝试通过以下几种方法来解决它:

¥If you identified that duplication is the issue that you are encountering there are several things you can try to solve it:

如果你使用的是 npm,你可以尝试运行 npm dedupe。此命令搜索本地依赖,并尝试通过将公共依赖进一步移到树上来简化结构。

¥If you are using npm you can try running npm dedupe. This command searches the local dependencies and tries to simplify the structure by moving common dependencies further up the tree.

Note

请注意,npm dedupe 不适用于符号链接文件夹(即,当你使用 npm link 时)

¥Be aware that npm dedupe doesn't work well with symlinked folders (i.e., when you use npm link)

如果你使用的是 webpack,你可以更改 resolvestyled-components 模块的方式。你可以覆盖 webpack 查找依赖的默认顺序,并使你的应用 node_modules 比默认节点模块解析顺序更优先:

¥If you are using webpack, you can change the way it will resolve the styled-components module. You can overwrite the default order in which webpack will look for your dependencies and make your application node_modules more prioritized than default node module resolution order:

resolve: {
+   alias: {
+     "styled-components": path.resolve(appFolder, "node_modules", "styled-components"),
+   }
  }

与 Lerna 一起使用(Usage with Lerna)

¥Usage with Lerna

要使样式化组件在 Lerna monorepo 中跨包运行,一种可能的修复方法是对 monorepo 文件的根共享 hoist 依赖。尝试使用 --hoist 标志运行引导选项。

¥One possible fix to get styled-components to run in a Lerna monorepo across packages, is to hoist shared dependencies to the root of your monorepo file. Try running the bootstrap option with the --hoist flag.

lerna bootstrap --hoist

或者,你可以从 package.json 文件中删除 styled-components 并将其手动提升到顶层 package.json 文件。

¥Alternatively, you can remove styled-components from your package.json file and hoist it manually to your top-level package.json file.

Lerna 根文件夹中的 package.json 文件示例

¥Example of a package.json file in a Lerna root folder

{
  "name": "my-styled-monorepo",
  "devDependencies": {
    "lerna": "3.6.0"
  },
  "dependencies": {
    "styled-components": "3.4.5"
  },
  "scripts": {
    "bootstrap": "lerna bootstrap",
    "clean": "lerna clean",
    "start": "lerna run start",
    "build": "lerna run build"
  }
}

为什么我应该避免在渲染方法中声明样式组件?(Why should I avoid declaring styled components in the render method?)

¥Why should I avoid declaring styled components in the render method?

通过在 React 组件的 render 方法中声明样式组件,你可以在每次渲染时动态创建一个新组件。这意味着 React 将不得不在每次后续渲染时丢弃并重新计算 DOM 子树的该部分,而不是仅仅计算它们之间发生变化的差异。这会导致性能瓶颈和不可预测的行为。

¥By declaring a styled component inside the render method of a react component, you are dynamically creating a new component on every render. This means that React will have to discard and re-calculate that part of the DOM subtree on each subsequent render, instead of just calculating the difference of what changed between them. This leads to performance bottlenecks and unpredictable behavior.

🚫

const Header = () => {
  const Title = styled.h1`
    font-size: 10px;
  `


  return (
    <div>
      <Title />
    </div>
  )
}

const Title = styled.h1`
  font-size: 10px;
`


const Header = () => {
  return (
    <div>
      <Title />
    </div>
  )
}

为什么我会收到 HTML 属性警告?(Why am I getting HTML attribute warnings?)

¥Why am I getting HTML attribute warnings?

下面的警告消息表明非标准属性被附加到 HTML DOM 元素,例如 <div><a>。如果你看到此警告消息,则你或你正在使用的库可能正在将 props 作为属性附加到 HTML DOM 元素。

¥The warning message below indicates that non-standard attributes are being attached to HTML DOM elements such as <div> or <a>. If you are seeing this warning message, it is likely that you or a library you are using is attaching props as attributes to HTML DOM elements.

Warning: Received "true" for a non-boolean attribute

如果你看到此警告,你可能正在通过 true,其中 "true" 是合适的。这可能来自 .attrs 属性,或者来自你传递给 styled(Component) 组件的完全不相关的 prop。

¥If you're seeing this warning you are probably passing true where "true" would be appropriate. It's likely that this comes from a .attrs property, or from a completely unrelated prop that you're passing to a styled(Component) component.

要了解有关如何传递 props 的更多信息,请参阅 本节

¥To learn more about how props are passed, see this section.

例如:

¥For example:

const Link = props => (
  <a {...props} className={props.className}>
    {props.text}
  </a>
)


const StyledComp = styled(Link)`
  color: ${props => (props.red ? 'red' : 'blue')};
`


<StyledComp text="Click" href="https://www.styled-components.com/" red />

这将渲染:

¥This will render:

<a text="Click" href="https://www.styled-components.com/" red="true" class="[generated class]">Click</a>

React 将警告附加的非标准属性,例如 "red" 和 "text",它们对于 <a> 元素来说不是有效的 HTML 属性。

¥React will warn on non-standard attributes being attached such as "red" and "text", which are not valid HTML attributes for the <a> element.

要解决此问题,你可以使用瞬态属性或解构属性:

¥To fix this, you can use transient props or destructure props:

瞬态属性(自 5.1 起)(transient props (since 5.1))

¥transient props (since 5.1)

你可以使用 瞬态属性 来解决此问题:

¥You can use transient props to fix this:

const Link = ({ className, text, ...props }) => (
  <a {...props} className={className}>
    {text}
  </a>
)


const StyledComp = styled(Link)`
  color: ${props => (props.$red ? 'red' : 'blue')};
`


<StyledComp text="Click" href="https://www.styled-components.com/" $red />

解构属性(destructure props)

¥destructure props

如果你使用 < 5.1 的版本或者无法使用瞬态属性,则可以使用参数解构来提取那些已知的样式属性:

¥If you use a version < 5.1 or if you can't use transient props, you can use argument destructuring to pull out those known styling props:

const Link = ({ className, red, text, ...props }) => (
  <a {...props} className={className}>
    {text}
  </a>
)


const StyledComp = styled(Link)`
  color: ${props => (props.red ? 'red' : 'blue')};
`


<StyledComp text="Click" href="https://www.styled-components.com/" red />

这将渲染:

¥This will render:

<a href="https://www.styled-components.com/" class="[generated class]">Click</a>

当你使用参数解构时,从 props 对象中提取的任何变量在扩展应用剩余的 props 时都不会被包含在内 (...props);

¥When you use argument destructuring, any variables pulled out of the props object will not be included when spread-applying the remaining props (...props);

支持哪些浏览器?(Which browsers are supported?)

¥Which browsers are supported?

styled-components 支持与当前 React 版本相同的浏览器集。

¥styled-components supports the same set of browsers as the current React version.

  • v2.x(反应 v0.14+):IE9+,所有常青浏览器

    ¥v2.x (React v0.14+): IE9+, all evergreen browsers

  • v3.x(反应 v0.14+):IE9+,所有常青浏览器

    ¥v3.x (React v0.14+): IE9+, all evergreen browsers

  • v4.x(反应 v16.3+):IE11、IE 9+(带有 Map + Set 填充),所有常青浏览器

    ¥v4.x (React v16.3+): IE11, IE 9+ (with Map + Set polyfills), all evergreen browsers

  • v5.x(反应 v16.3+):IE11,所有常青浏览器

    ¥v5.x (React v16.3+): IE11, all evergreen browsers

常青浏览器包括 Chrome 和 Firefox(及其衍生产品),因为无论操作系统版本如何,它们都可以更新。Edge 和 Safari 应该也可以正常工作,因为过去几年的所有版本都支持相关 API。

¥Evergreen browsers include Chrome and Firefox (and derivatives) as they can be updated regardless of operating system version. Edge and Safari should both also work fine since all versions for the last several years support the relevant APIs.

如何将样式组件与 create-react-app 一起使用?(How do I use styled-components with create-react-app?)

¥How do I use styled-components with create-react-app?

该库的基本功能应该像任何其他库一样开箱即用。

¥The basic functionality of the library should work out of the box like any other library.

但是,如果你想进行服务器端渲染或利用 styled-components babel 插件 的一些高级功能而不弹出,则需要设置 react-app-rewiredreact-app-rewire-styled-components

¥However, if you want to do server-side rendering or take advantage of some of the advanced capabilities of the styled-components babel plugin without ejecting you'll need to set up react-app-rewired and react-app-rewire-styled-components.

如何解决使用 npm linkyarn link 时的问题?(How can I fix issues when using npm link or yarn link?)

¥How can I fix issues when using npm link or yarn link?

本地链接可以成为同时共同开发项目的有用工具。然而,它会造成混乱的情况,因为你的每个本地项目都可能下载了一整套开发依赖(默认情况下,打包程序更喜欢本地版本的依赖),而这些库本来是用作单例(例如 React 和 styled-components)的。

¥Local linking can be a useful tool to co-develop projects simultaneously. However, it creates chaotic situations with libraries that are meant to be used as singletons like react and styled-components since each of your local projects likely has a full set of development dependencies downloaded (and bundlers prefer local versions of dependencies by default.)

解决方案是添加别名。这是 webpack 的示例配置:

¥The solution is to add aliasing. Here's an example config for webpack:

// const path = require('path');


{
  resolve: {
    alias: {
      // adjust this path as needed depending on where your webpack config is
      'styled-components': path.resolve('../node_modules/styled-components')
    }
  }
}

这可以确保你的构建始终使用相同的库副本,即使是在符号链接的项目中也是如此。

¥This ensures that for your build the same copy of the library will always be used, even across symlinked projects.

SSR 场景中的链接。(Linking in an SSR SCENARIO.)

¥Linking in an SSR SCENARIO.

如果你在具有链接组件的项目上使用 collectStyles 函数,你最终会遇到复杂的情况。基本上发生的情况是,由于 v4 新的静态上下文 API,不同的样式组件模块现在正在管理自己的要渲染的样式组件列表,从主机应用看来好像没有什么可提取的,因为没有在其中创建样式组件 它们是从链接的包范围创建的范围。

¥If you are using the collectStyles function on a project with linked components you will end up in a complex scenario. Basically what's happening is that because of v4 new static context API different styled-component modules are now managing their own list of styled-components to render, from the host app it appears as though there's nothing to extract because no styled components have been created in that scope they were created from the linked package scope.

我该如何解决?(How do I solve it?)

¥How do I solve it?

一种解决方案是向 styled-components 模块路径解析添加别名,以始终指向 'host' 应用。希望有很多库可供我们用于此示例 module-alias。在 SSR 索引文件的最顶部添加:

¥One solution is to add an alias to the styled-components module path resolution to always point to the 'host' application. Hopefully there are a bunch of libraries to do that we will use for this example module-alias. At the very top of your SSR index file add:

const path = require('path');
const moduleAlias = require('module-alias');


moduleAlias.addAlias('styled-components', path.join(__dirname, '../node_modules/styled-components'));

这将告诉节点将样式组件的所有导入/需求解析为 __dirname, '../node_modules/styled-components'

¥This will tell node to resolve all import/require's of styled-components to __dirname, '../node_modules/styled-components'

如何修复服务器端渲染后闪烁的文本?(How do I fix flickering text after server side rendering?)

¥How do I fix flickering text after server side rendering?

当使用 createGlobalStyle 或之前的 injectGlobal 等全局样式 API 时,从 DOM 中添加和删除某些样式(如 @font-face 定义)可能会导致页面上的文本短暂闪烁。这通常发生在服务器端渲染的补水阶段。我们仍在调整这些行为的工作方式,以避免长期出现该问题。

¥When using global styling APIs like createGlobalStyle or the former injectGlobal, adding and removing certain styles from the DOM like @font-face definitions can cause momentary flickering of text on the page. This typically happens during the rehydration phase of server-side rendering. We're still tweaking how these behaviors work to avoid the issue long-term.

不过,font-display CSS 规则 中有一个 CSS 解决方案来解决这个问题。通过将规则设置为 "fallback" 模式,一旦加载字体,就不会重新加载。这消除了闪烁。

¥However, there is a CSS solution to the problem in the font-display CSS rule. By setting the rule to "fallback" mode, once a font has been loaded it will not be reloaded. This eliminates the flicker.

@font-face {
  font-family: 'Foo';
  src: url('/path/to/foo.woff') format('woff');
  font-style: normal;
  font-weight: 400;
  font-display: fallback; /* <- this can be added to each @font-face definition */
}

缺少 styled-components/native 的声明?(Missing Declarations for styled-components/native?)

¥Missing Declarations for styled-components/native?

如果你收到错误消息:

¥If you're getting the error message:

Could not find a declaration file for module 'styled-components/native'

在使用 TypeScript 的 React Native 项目中,是因为需要添加 @types/styled-components-react-native

¥In a React Native project using TypeScript, it is because you need to add @types/styled-components-react-native.