styled-components 是对如何增强 CSS 以为 React 组件系统添加样式的思考结果。 通过专注于单一用例,我们成功优化了开发人员的体验以及最终用户的输出。
除了改善开发人员的体验之外,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 插件。它提供了许多优势,例如更易读的类名、服务器端渲染兼容性、更小的包等等。
如果您没有使用模块打包器或包管理器,我们还提供了一个托管在 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 调整它。
此按钮组件具有一个主要状态,该状态会更改其颜色。将 $primary
prop 设置为 true 时,我们正在交换其背景色和文本颜色。
您可能经常想要使用一个组件,但将其稍微更改以适应特定情况。现在,您可以传递一个插值函数并根据某些 props 进行更改,但这对于覆盖一次样式来说太麻烦了。
为了轻松地创建一个继承另一个组件样式的新组件,只需将其包装在 styled()
构造函数中即可。这里我们使用上一节中的按钮,并创建一个特殊的按钮,通过一些与颜色相关的样式来扩展它
我们可以看到,新的 TomatoButton
仍然类似于 Button
,而我们只添加了两个新规则。
在某些情况下,您可能想要更改样式化组件渲染的标签或组件。例如,在构建导航栏时,这很常见,因为其中包含链接和按钮的混合,但它们应该具有相同的样式。
针对这种情况,我们有一个逃生舱。您可以使用 "as" 多态 prop
动态地交换接收您编写的样式的元素
这对于自定义组件也能完美地工作!
styled
方法适用于您自己的所有组件或任何第三方组件,只要它们将传递的 className
prop 附加到 DOM 元素即可。
如果您使用的是 react-native
,请记住使用 style
而不是 className
。
您也可以将标签名称传递到 styled()
工厂调用中,如下所示:styled("div")
。实际上,styled.tagname
助手只是执行相同操作的别名。
如果样式化的目标是简单元素(例如 styled.div
),styled-components 会将任何已知的 HTML 属性传递到 DOM。如果是自定义 React 组件(例如 styled(MyComponent)
),styled-components 会将所有 props 传递过去。
此示例显示了 Input 组件的所有 props 如何传递到已安装的 DOM 节点,就像 React 元素一样。
注意 $inputColor
属性没有传递给 DOM,而 type
和 defaultValue
是传递了的?styled
函数足够智能,可以自动为您过滤非标准属性。
如果您熟悉将 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 方法之外定义样式化组件非常重要,否则它将在每次渲染过程中被重新创建。在 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 Marcassa 在Styled 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
如果您在没有与号的情况下放置选择器,它们将引用组件的子元素。
为了避免不必要的包装器,这些包装器只是将一些属性传递给渲染的组件或元素,您可以使用 .attrs
构造函数。它允许您将其他属性(或 "属性")附加到组件。
这样,您就可以例如将静态属性附加到元素,或将像 activeClassName
这样的第三方属性传递给 React Router 的 Link 组件。此外,您还可以将更多动态属性附加到组件。.attrs
对象还接受函数,这些函数接收组件接收的属性。返回值也将合并到结果属性中。
这里,我们渲染了一个 Input
组件,并为其附加了一些动态和静态属性
如您所见,我们可以在插值中访问新创建的属性,并且 type
属性被传递给元素。
请注意,当包装样式化组件时,.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
助手!
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
除了设置 flexGrow
为 1
和 flexBasis
为 0
外,还会设置 flexShrink
为 1
。
想象一下您在 React Native 中如何编写此属性,猜测您如何将其转换为 CSS,您可能是对的
const RotatedBox = styled.View` transform: rotate(90deg); text-shadow-offset: 10px 5px; font-variant: small-caps; margin: 5px 7px 2px; `
与 Web 版本的一些区别在于,您不能使用 keyframes
和 createGlobalStyle
助手,因为 React Native 不支持关键帧或全局样式。如果您使用媒体查询或嵌套 CSS,我们也会发出警告。
在 v2 中,我们支持百分比。为了实现这一点,我们需要为所有简写强制执行单位。如果您要迁移到 v2,可以使用代码修改工具。
如果您更愿意只导入 styled-components
而不是 styled-components/native
,您可以添加一个 resolverMainFields
配置,其中包含 "react-native"
。这以前在 metro 中默认支持(并且目前在 haul 中确实有效),但似乎在某个时刻被删除了。