首先,让我们从更新必要的包到最新版本开始。
如果您使用的是 NPM
npm install styled-components@^6.0.0 stylis@^4.0.0 npm uninstall @types/styled-components
如果您使用的是 Yarn
yarn add styled-components@^6.0.0 stylis@^4.0.0 yarn remove @types/styled-components
由于 styled-components 现在提供自己的类型,因此不再需要社区类型。
对于 TypeScript 爱好者来说,好消息 - styled-components 现在是用 TypeScript 原生编写的!即使您以前没有使用过 TypeScript,也建议使用它来提高项目的可靠性,因为它可以在您使用未知 prop 或 prop 的值与预期值不同时提醒您。
但是,如果您在项目中没有使用 TypeScript,不用担心!像 VS Code 这样的 IDE 仍然会识别类型,并在您编写代码时提供提示。
shouldForwardProp
不再默认提供如果您尚未迁移您的样式以使用 瞬态 props ($prefix
),您可能会注意到 React 警告,表明样式 props 传递到了 v6 中的 DOM。要恢复 v5 的行为,请使用 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; }
随着网络和浏览器的成熟,到 2023 年,供应商前缀通常是不必要的。因此,在 v6 版本中,我们决定默认情况下省略自动前缀,以减少传递到页面的 CSS 量。如果您更喜欢 v5 的行为,可以通过 StyleSheetManager
恢复它。
import { StyleSheetManager } from 'styled-components'; function MyApp() { return ( <StyleSheetManager enableVendorPrefixes> {/* other providers or your application's JSX */} </StyleSheetManager> ) }
为了适应此更改,原始的 disableVendorPrefixes
prop 被反转为 enableVendorPrefixes
;如果您设置了 disableVendorPrefixes
,现在可以将其删除,因为它现在是默认值。
styled-components v6 使用更新的 stylis v4;如果您提供 stylisPlugins
到 StyleSheetManager
,请确保插件是最新的。例如,stylis-plugin-rtl
发布了一个新版本来支持更新的 stylis。
随着 stylis v4 升级 对嵌套选择器处理方式进行了更改,现在它正确地反映了浏览器的行为。具体来说,不以 &
开头的伪选择器(例如 :before
)不再隐式添加 & 符号。
v5 行为
styled.div` :hover { color: red; } ` // .[classname]:hover { color: red; } styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
v6 行为
styled.div` :hover { color: red; } ` // .[classname] :hover { color: red; } (equivalent to .[classname] *:hover) styled.div` &:hover { color: red; } ` // .[classname]:hover { color: red; }
$as
和 $forwardedAs
props 已被删除为了减少应用顺序方面的困惑,我们删除了瞬态 $as
和 $forwardedAs
props。请使用常规的 as
和 forwardedAs
props 代替。
withComponent()
API这个变化由来已久。 withComponent
API 不再受支持,因此请使用 as
prop 代替。您可以在定义时通过 attrs
指定 as
,也可以在运行时指定
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.npmjs.net.cn"> <ButtonLink href="https://styled-components.npmjs.net.cn">
根据 Node 的维护时间表,我们现在支持 v16 作为仍在接收安全补丁的最旧运行时。
准备好迎接挑战了吗?
npm install styled-components@^5.0.0 react@^16.8 react-dom@^16.8 react-is@^16.8
如果您使用的是 React Native,您需要至少 v0.59(第一个支持 hooks 的版本)。
就是这样。 💥
styled-components v5 没有引入任何破坏性的公共 API 更改,并添加了以下内容
核心样式表引擎的完全重写,针对性能进行了优化
新的基于 hooks 的组件模型
StyleSheetManager
具有新的 props
disableCSSOMInjection
disableVendorPrefixes
stylisPlugins
stylis-plugin-rtl
尝试一下您的双向需求!注意:v4 中已弃用的子函数对象形式 .attrs({ prop: props => {} })
语法在 v5 中已删除。请改用函数形式的 attrs .attrs(props => ({}))
(您应该已经看到控制台警告,以便提前进行此更新)。
查看 官方公告帖子,以获取更多信息,并了解 v5 中的改进内容!
更新至 jest-styled-components v7
npm install jest-styled-components@^7.0.0
@import
和 createGlobalStyle
的注意事项目前,我们不建议在 cGS 中使用 @import
,因为浏览器通过 CSSOM API 处理 @import
时存在一些问题。相反,最好将这些代码放在您的核心 index.html
文件(生成的或静态的)中,位于典型的 <style>
标签内。
这是一个非常大的版本,在内部和 API 层面都进行了很多更改。随着测试版的进行,我们将尝试发布代码修改以简化以下内容。此外,如果您在以下步骤中发现任何问题,请留下建设性的反馈!
npm install styled-components@^4.0.0
react
>= 16.3; 在内部,我们使用新的 React.forwardRef
API 和新的上下文 API,如果您希望尝试为旧的 React 版本支持进行填充npm install react@^16.3 react-dom@^16.3
如果您使用的是 enzyme
或其他依赖项(如 react-test-renderer
),如果您来自旧版本的 react
,可能还需要完成更多相关的升级。
.extend
API,请将您的组件切换为使用 styled(StyledComponent)
。有代码修改可用来加快此过程。
🚫
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; `
有关更多示例,请参阅“扩展样式”文档。
injectGlobal
API 将全局样式添加到您的页面,请使用新的createGlobalStyle
辅助组件。有代码修改可用来加快此过程。
🚫
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
中无法使用!
innerRef
属性,请将其更改为普通的 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>
css
辅助函数的情况下在部分中使用 keyframes
组件,则现在需要使用辅助函数。一般来说,在将样式部分组合到样式化组件中时,始终使用 css 辅助函数。🚫
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}; `
attrs({})
,并且您传递给它的某些属性是函数,建议您切换到新的 attrs(props => ({}))
语法,以便更轻松地进行更强大的组合。🚫
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; `
npm install @types/styled-components
就是这样!除了迁移之外,我们还强烈建议您阅读有关新的"as" 属性
的内容,该属性旨在将来替换withComponent API
。
是的:嵌套是特意从 Sass 中移植过来的功能。谨慎使用,它是一种通过减少对每个元素创建显式类的需求来简化代码的好方法。
它还可以由父组件用来定义上下文约束,这些约束不是受影响子组件的真正问题。
FirstSecondThirdFirstSecondThird
它也极其方便用来将媒体查询放在一起,因为我们可以一目了然地看到组件在任何分辨率下的响应方式。
Hello world!
将现有的 CSS 框架与 styled-components 集成非常容易!您可以将它的现有类名与您的组件一起使用。
例如,假设您有一个现有应用程序,其中包含两个您想再次使用的类:.small
和 .big
。如果您希望该类始终附加到组件,则应使用attrs
方法 将其附加。如果您希望仅在某些情况下附加它,则可以使用 className
属性,就像您一直使用的那样!
如果该框架有许多需要包含在页面上的原始全局 CSS,您可以使用createGlobalStyle
API 添加它。这对于 CSS 重置之类的操作也很有用。
请注意,对于 styled-components v3 及以下版本,全局样式的先前 API 是injectGlobal
。
使用高特异性覆盖样式的方法是简单地提高您自己的样式的特异性。这可以通过使用 !important
来完成,但这容易出错,而且通常不是一个好主意。
我们建议使用以下技术
const MyStyledComponent = styled(AlreadyStyledComponent)` &&& { color: #BF4F74; font-weight: bold; } `
每个 &
将被生成的类替换,因此注入的 CSS 将如下所示
.MyStyledComponent-asdf123.MyStyledComponent-asdf123.MyStyledComponent-asdf123 { color: #BF4F74; font-weight: bold; }
重复的类会将特异性提高到足以覆盖源顺序,而不会非常繁琐地编写!
内联样式始终优先于外部 CSS,因此您无法通过简单地提高特异性来覆盖它。
但是,有一个巧妙的技巧,就是将 style element-attr
CSS 选择器与 !important
结合使用
const MyStyledComponent = styled(InlineStyledComponent)` &[style] { font-size: 12px !important; color: blue !important; } `
每个节点实际上都连接了两个类:一个是每个组件的静态类,这意味着样式化组件的每个元素都有此类。它没有附加任何样式。相反,它用于快速识别 DOM 对象属于哪个样式化组件,或在 DevTools 中进行细微更改。它也用于组件选择器。静态类可能看起来像:.sc-fVOeaW
。
另一个是动态的,这意味着它对于具有不同道具的样式化组件的每个元素都不同,具体取决于插值的结果。它可能看起来像 .fVOeaW
(注意缺少 "sc" 前缀)。
例如,样式化组件 <Button />
将始终使用相同的静态类呈现。如果使用插值更改样式,例如 <Button secondary />
,则动态类将不同,而静态类将保持不变。
您可以使用attrs 将属性传递给样式化组件,但这并不总是明智的。
经验法则是:当您希望样式化组件的每个实例都具有该属性时,使用 attrs
;当每个实例都需要一个不同的属性时,直接传递属性。
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" />
对于可以根据另一个属性的“模式”推断出的属性,也是如此。在这种情况下,您可以将 attrs
上的一个属性设置为一个函数,该函数根据其他属性计算该属性。
styled-components
与我的库捆绑在一起吗?如果您是一个库作者,我们建议您不要将 styled-components
模块与您的库捆绑在一起并发布。您需要执行两个步骤来实现这一点
styled-components
标记为外部依赖项styled-components
styled-components
标记为外部依赖项为此,您需要将其从 dependencies
移动到devDependencies
,并在您的 package.json
文件中的peerDependencies
列表中包含它
{ - "dependencies" : { + "devDependencies" : { "styled-components": "^3.4.9" }, + "peerDependencies" : { + "styled-components": ">= 3" + } }
将 styled-components
移至 devDependencies
将确保它不会与您的库一起安装(npm install
或 yarn add
在安装库时会忽略 devDependencies
)。
将 styled-components
添加到 peerDependencies
将向您的库使用者发出信号,表明 styled-components
不包含在库中,他们需要自己安装。
此外,请注意,在 peerDependencies
部分中,版本字符串已更改为更宽松的 >= 3
。这允许 styled-components 的未来版本自动工作,并且如果最终添加了重大更改,您只需通过对您的库进行补丁更新来缩小范围。
styled-components
如果您在发布库之前对其进行捆绑,请确保您没有将 styled-components
与之捆绑在一起。以下是一些使用一些流行的模块捆绑工具来执行此操作的示例。
如果您使用的是 Microbundle,它将自动处理此步骤。Microbundle 将 peerDependencies
列表中的每个依赖项视为外部依赖项,并将其从构建中排除。
如果您使用的是 Rollup.js,您应该在配置中提供一个 external
选项。
export default { entry: "my-awesome-library.js", + external: [ + "styled-components" + ] }
另一种方法是使用 rollup-plugin-peer-deps-external 插件,它会自动为您将 peerDependencies
添加到 external
选项数组中。
+ import peerDepsExternal from 'rollup-plugin-peer-deps-external'; export default { entry: "my-awesome-library.js", + plugins: [ + // Preferably set as first plugin. + peerDepsExternal(), + ] }
如果您使用的是 Webpack,您应该在配置中提供一个 externals
选项。
modules.export = { entry: "my-awesome-library.js", + externals: { + "styled-components": { + commonjs: "styled-components", + commonjs2: "styled-components", + amd: "styled-components", + }, + }, }
您可以在 Webpack 文档的 "编写库" 部分找到有关如何使用 Webpack 捆绑库的更多有用信息。
如果您在控制台中看到类似于下面的警告消息,您可能在页面上初始化了多个 styled-components
实例。
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.
这可能会导致动态样式无法正常工作,甚至在使用服务器端渲染时出现错误。
造成这种情况的几个常见原因:
styled-components
的应用程序在同一个页面上运行(例如,在同一个页面上加载了 webpack 中的多个入口点)。styled-components
库。styled-components
模块在多个包中都是依赖项(这与前一个原因大体相同)。如果您在同一个页面上运行了多个应用程序,请考虑对它们全部使用一个 styled-components
模块。如果您使用的是 webpack,您可以使用 CommonsChunkPlugin 来创建一个 显式供应商块,其中将包含 styled-components
模块。
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
中重复的模块如果您认为问题出在依赖项中的某个地方存在重复的 styled-components
模块,您可以通过多种方式来检查。您可以在应用程序文件夹中使用 npm ls styled-components
、yarn list --pattern styled-components
或 find -L ./node_modules | grep /styled-components/package.json
命令。
如果这些命令都没有识别出重复,请尝试分析您的捆绑包,查看是否存在多个 styled-components
实例。您只需检查捆绑包的源代码,或者使用 source-map-explorer 或 webpack-bundle-analyzer 等工具。
如果您确定重复是您遇到的问题,您可以尝试几种方法来解决它。
如果您使用的是 npm
,您可以尝试运行 npm dedupe
。此命令会搜索本地依赖项并尝试通过将公共依赖项向上移动到树中来简化结构。
请注意,npm dedupe
无法很好地处理符号链接文件夹(即,当您使用 npm link
时)。
如果您使用的是 webpack,您可以更改它 解析 styled-components
模块的方式。您可以覆盖 webpack 搜索依赖项的默认顺序,并使您的应用程序 node_modules
比默认的节点模块解析顺序优先级更高。
resolve: { + alias: { + "styled-components": path.resolve(appFolder, "node_modules", "styled-components"), + } }
在 Lerna 单仓库中跨包运行 styled-components 的一种可能的解决方法是将共享的依赖项 提升 到您的单仓库文件的根目录。尝试使用 --hoist 标志运行 bootstrap 选项。
lerna bootstrap --hoist
或者,您可以从 package.json
文件中删除 styled-components
,并将其手动提升到您的顶级 package.json 文件中。
Lerna 根文件夹中 package.json 文件的示例
{ "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" } }
通过在 React 组件的渲染方法中声明 styled 组件,您将在每次渲染时动态创建一个新组件。这意味着 React 将不得不丢弃并重新计算 DOM 子树中的那部分,而不是仅计算它们之间更改的差异。这会导致性能瓶颈和不可预测的行为。
🚫
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 DOM 元素,例如 <div>
或 <a>
。如果您看到此警告消息,则很可能是您或您使用的库将属性作为属性附加到 HTML DOM 元素。
Warning: Received "true" for a non-boolean attribute
如果您看到此警告,则很可能是您将 true
传递到 "true"
应该出现的地方。这很可能来自 .attrs
属性,或者来自您传递给 styled(Component)
组件的完全无关的属性。
要了解有关如何传递属性的更多信息,请参阅 本节。
例如
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 />
这将呈现
<a text="Click" href="https://www.styled-components.com/" red="true" class="[generated class]">Click</a>
React 将对附加的非标准属性发出警告,例如 "red" 和 "text",它们不是 <a>
元素的有效 HTML 属性。
要解决此问题,您可以使用瞬态属性或解构属性
您可以使用 瞬态属性 来解决这个问题
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 />
如果您使用的是低于 5.1 的版本,或者您不能使用瞬态属性,您可以使用参数解构来提取这些已知的样式属性
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 />
这将呈现
<a href="https://www.styled-components.com/" class="[generated class]">Click</a>
当您使用参数解构时,从 props 对象中提取的任何变量都不会在将剩余 props 扩展应用时包含在内 (...props
);
styled-components 支持与当前 React 版本相同的浏览器集。
常青浏览器包括 Chrome 和 Firefox(及其衍生产品),因为它们可以更新,而无需考虑操作系统版本。Edge 和 Safari 也应该可以正常工作,因为过去几年中所有版本都支持相关的 API。
create-react-app
一起使用? 库的基本功能应该像任何其他库一样开箱即用。
但是,如果您想进行服务器端渲染或利用 styled-components babel 插件 的一些高级功能,而无需弹出,则需要设置 react-app-rewired
和 react-app-rewire-styled-components
。
npm link
或 yarn link
时出现的问题? 本地链接可以是同时开发项目的有效工具。但是,它会为旨在用作单例的库(如 react 和 styled-components)创建混乱情况,因为您的每个本地项目可能都下载了一整套开发依赖项(并且捆绑程序默认情况下更喜欢本地版本的依赖项)。
解决方案是添加别名。以下是一个 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') } } }
这确保了在构建过程中始终使用库的同一份副本,即使跨符号链接项目也是如此。
如果您在链接组件的项目上使用 collectStyles
函数,您将最终陷入一个复杂的情况。基本上,发生的事情是,由于 v4 新的静态上下文 API,不同的 styled-component 模块现在管理自己的 styled-components 列表以进行渲染,从主机应用程序看来,似乎没有任何东西可以提取,因为没有在该范围内创建 styled-components,它们是从链接的包范围内创建的。
一种解决方案是将别名添加到 styled-components
模块路径解析中,以始终指向“主机”应用程序。希望有很多库可以做到这一点,我们将在本示例中使用 module-alias
。在您的 SSR 索引文件的顶部添加
const path = require('path'); const moduleAlias = require('module-alias'); moduleAlias.addAlias('styled-components', path.join(__dirname, '../node_modules/styled-components'));
这将告诉 node 将所有对 styled-components 的 import/require 解析为 __dirname, '../node_modules/styled-components'
当使用全局样式 API(如 createGlobalStyle
或以前的 injectGlobal
)时,从 DOM 中添加和删除某些样式(如 @font-face
定义)会导致页面上文本的短暂闪烁。这通常发生在服务器端渲染的重新水合阶段。我们仍在调整这些行为的工作方式,以避免长期出现此问题。
但是,font-display
CSS 规则 中有一个 CSS 解决方案可以解决此问题。通过将规则设置为“fallback”模式,一旦字体加载,它就不会重新加载。这消除了闪烁。
@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
的声明? 如果您收到错误消息
Could not find a declaration file for module 'styled-components/native'
在使用 TypeScript 的 React Native 项目中,这是因为您需要添加 @types/styled-components-react-native
。