使用 styled-components 定义组件样式

styled-components 是一个常用的 css in js 类库。和所有同类型的类库一样,通过 js赋能解决了原生 css所不具备的能力,比如变量、循环、函数等。

动机

  • 自动关联css
  • 可以在样式定义中直接引用到 js 变量,共享变量
  • 自动生成独立的类名,避免重复、重叠或拼写错误
  • 简单的动态样式,不用写很多类名
  • 支持组件之间继承,方便代码复用,提升可维护性
  • 方便样式维护,我们只需定位到某个组件,就能快速改变其样式

安装

执行以下命令便能快速安装依赖:

npm install --save styled-components

或者

yarn add styled-components

基本用法

styled-components使用标签模板来对组件进行样式化。

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

下面我们将写两个简单的组件来说明,一个容器组件Wrapper,一个标题H1

Wrapper.js

import styled from "styled-components";

const Wrapper = styled.div`
  height: 200px;
  width: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
  background: aqua;
`;

export default Wrapper;

H1.js

import styled from "styled-components";

const H1 = styled.h1`
  font-size: 32px;
  font-weight: bolder;
  color: chocolate;
`;

export default H1;

App.js

import React from "react";
import ReactDOM from "react-dom";
import Wrapper from "./Wrapper";
import H1 from "./H1";

function App() {
  return (
    <Wrapper>
      <H1>Hello,This is a demo of style components!</H1>
    </Wrapper>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

运行程序,我们就能看到如下效果:

style-components-demo.png

此时,h1应用了我们定义的样式。

基于Props做样式判断

模板标签的函数插值能拿到样式组件的 props,可以据此调整我们的样式规则。

第一种方式:

Button.js

import styled from "styled-components";

const Button = styled.button`
  min-width: 64px;
  background: ${props => (props.primary ? "blue" : "transparent")};
  color: ${props => (props.primary ? "white" : "palevioletred")};
  font-size: 14px;
  margin: 8px;
  padding: 8px;
  border: ${props => (props.primary ? "none" : `2px solid palevioletred`)};
  border-radius: 3px;
`;

export default Button;

上述示例中如果primary属性存在,则按钮背景色会变成蓝色,边框消失,并且文字颜色变成白色,使用方式及效果如下:

<div>
  <Button>Normal Button</Button>
  <Button primary>Primary Button</Button>
</div>

运行效果:

primary-button.png

此外,我们还可以使用css定义一个样式,然后根据属性判断来调整我们的样式规则,这就有了第二种方式:

Button.js

import styled, { css } from "styled-components";

const disabledStyle = css`
  background: transparent;
  color: rgba(0, 0, 0, 0.38);
  border: 2px solid rgba(0, 0, 0, 0.38);
`;

const Button = styled.button`
  min-width: 64px;
  background: ${props => (props.primary ? "blue" : "transparent")};
  color: ${props => (props.primary ? "white" : "palevioletred")};
  font-size: 14px;
  margin: 8px;
  padding: 8px;
  border: ${props => (props.primary ? "none" : `2px solid palevioletred`)};
  border-radius: 3px;
  ${props => props.disabled && disabledStyle};
`;

export default Button;

此时,如果我们给 Button 组件一个disabled属性,则disabledStyle中的样式会自动覆盖原有样式中对应的部分。

<Button disabled>Disabled Button</Button>

运行效果:

disabled-button.png

最后,我们还可以直接传入一个样式属性来控制组件样式的规则,比如,我们希望能自定义按钮的最小宽度,此时我们可以调整Button.js为:

import styled from "styled-components";

const Button = styled.button`
  min-width: ${props => props.minWidth || 64}px;
  background: ${props => (props.primary ? "blue" : "transparent")};
  color: ${props => (props.primary ? "white" : "palevioletred")};
  font-size: 14px;
  margin: 8px;
  padding: 8px;
  border: ${props => (props.primary ? "none" : `2px solid palevioletred`)};
  border-radius: 3px;
`;

export default Button;

此时,只要我们在使用 Button 组件时,给minWidth属性赋值,按钮就会按照我们制定的最小宽度渲染。

<Button minWidth={24}>Mini Button</Button>
mini-button.png

扩展样式

样式扩展主要针对当前组件有部分样式不满足需求的情况,此时我们可以通过样式扩展来进行样式调整,比如:我们希望上面例子中的 Button 组件的边框颜色和字体颜色变成蓝色,此时我们仅仅需要下面一小段代码调整即能满足需求:

const BlueButton = styled(Button)`
  color: blue;
  border: 2px solid blue;
`;
<BlueButton>Blue Button</BlueButton>
blue-button.png

在某些场景下,我们可能不仅仅只是想要修改组件的样式,甚至想要更新组件的渲染元素,styled-components曾经提供了一种方式来满足我们的需求,即.withComponent()方法。不幸的是在后续版本中,此方法将会被废弃。但令我们欣慰的是:styled-components最新版本为我们提供了一种新的方式,就是as属性。

假设,我们想要使用<a>来渲染我们的 Button 组件,我们仅仅需要在使用 Button 时,赋予一个as属性即可:

<Button as="a" href="https://www.styled-components.com">
  Link Button
</Button>

运行效果:

link-button.png

同样的,我们也可以使用我们自己定义的其它组件来给as属性赋值。

自定义任意组件的样式

styled-components实际上也是通过className的方式添加样式,所以,只要我们的组件有className,我们就能使用styled-components自定义其样式。

import React from "react";
import styled from "styled-components";

const P = ({ className, children }) => <p className={className}>{children}</p>;

const CustomP = styled(P)`
  color: blue;
  font-size: 32px;
`;

export { P, CustomP };

App.js

<div>
  <P>这是一段普通的文本内容</P>
  <CustomP>这是一段自定义样式的文本内容</CustomP>
</div>

运行效果:

custom-style.png

使用.attrs添加属性

我们可以使用attrsAPI 来为样式组件添加一些属性,它们也可以通过标签模板插值函数拿到 props 传值。

import styled from "styled-components";

const PasswordInput = styled.input.attrs({
  type: "password",
  margin: props => props.size || "1em",
  padding: props => props.size || "1em"
})`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
  margin: ${props => props.margin};
  padding: ${props => props.padding};
`;

export default PasswordInput;

App.js

<PasswordInput placeholder="请输入密码" size="0.25rem" />

运行效果:

password-input.png

动画

带有@keyframesCSS animations,一般来说会产生复用。styled-components暴露了一个keyframesAPI,我们使用它产生一个可以复用的变量。这样,我们在书写css样式的时候使用 JavaScript 的功能,为CSS附能,并且避免了名称冲突。

import styled, { keyframes } from "styled-components";

const rotateStyle = keyframes`
    from {
    transform: rotate(0deg);
  }

    to {
    transform: rotate(360deg);
  }
`;

const Rotate = styled.div`
  display: inline-block;
  animation: ${rotateStyle} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`;

export default Rotate;

App.js

<Rotate>旋转</Rotate>

运行效果:

rotate.gif

父组件中定义子组件样式

styled-components提供了component selector组件选择器模式来代替我们以往对 class 名的依赖。

开篇的示例,如果我们想要在 Wrapper 中改变H1的颜色为白色,可以有下面两种方式:

第一种,通过h1查找,并修改样式:

Wrapper.js

const Wrapper = styled.div`
  height: 200px;
  width: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
  background: aqua;

  > h1 {
    color: white;
  }
`;

第二种,通过H1的组件名查找元素并修改样式:

const Wrapper = styled.div`
  height: 200px;
  width: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
  background: aqua;

  ${H1} {
    color: white;
  }
`;

App.js

<Wrapper>
  <H1>Hello,This is a demo of style components!</H1>
</Wrapper>

以上两种方式运行效果如下:

css-in-parent.png

注意:下面这种方式不支持在父组件中定义自组件样式

class A extends React.Component {
  render() {
    return <div />;
  }
}

const B = styled.div`
  ${A} {
  }
`;

因为 A 继承ReactComponent,不是被 styled 构造过的。我们的组件选择器只支持在Styled Components创建的样式组件。

附注

有时候可能由于优先级的问题,我们自定义的样式无法使用,此时我们只需要使用&&就能提升其优先级:

const CustomButton = styled(Button)`
  && {
    width: 88px;
  }
`;

总结

这篇文章主要介绍了styled-components的基本用法,包括通过属性控制样式规则、样式扩展、自定义组件样式、使用.attrs添加属性、动画等,希望可以通过阅读此文章数量掌握如何使用styled-components定义组件样式。

查看示例源码

参考文章

styled-components 官网

Styled-Components

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容