styled-components的使用方法

styled-components

  • styled-components是一个React的第三方库,是CSS in JS的优秀实践。
  • 顾名思义,styled-components 以组件的形式来声明样式让样式也成为组件从而分离逻辑组件与展示组件。
  • styled-components 通过 模板字符串 来设置组件样式,它移除了组件和样式之间的映射。
  • 当我们通过styled-components定义样式时,我们实际上是创建了一个附加了样式的常规 React 组件
  • 可参考官方文档以及中文文档

CSS与CSS in JS

  • CSS的缺点
    1. 全局污染:CSS 选择器的作用域是全局的,所以很容易引起选择器冲突;而为了避免全局冲突,又会导致类命名的复杂度上升
    2. 复用性低:CSS 缺少抽象的机制,选择器很容易出现重复,不利于维护和复用
  • CSS in JS:随着组件化时代的来临,前端应用开始从组件的层面对 CSS 进行封装。
    1. 也就是通过 JS 来声明样式、抽象样式从而提高组件的可维护性
    2. 组件加载时动态的加载样式,动态生成类名从而避免全局污染

安装styled-components

项目中:

1
yarn add styled-components

使用styled-components

(例子可看官方文档

  1. 在style.js中定义样式组件
  2. 在需要使用组件的js文件中引入style.js,即可正常使用。

例子

通过styled在h1的基础上创建了Title组件,在section的基础上创建了Wrapper组件,然后使用这两个组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;

// Use Title and Wrapper like any other React component – except they're styled!
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>

效果


通过props在使用样式组件时调整样式

  • style.js样式的属性值可以是从使用组件中传递过来的属性值,这样使用同一个样式组件时可以通过传递不同的属性值达到不同的效果
    1. style.js中,使用 模板字符串 中的${(props)=>props.属性名}来设置样式属性值
    2. 使用组件时,通过<组件名 属性名={属性值}></组件名>将 属性值 传到style.js中作为 样式属性值

例子

下面这个 Button 组件上有一个可以改变color的primary属性,将其设置为 ture 时,组件的background-color和color会交换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Button = styled.button`
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};

font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;

render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);

效果


styled()样式继承 创建新组件

  • 希望使用某个经常使用的 自定义组件,在特定场景下可以稍微更改其样式.当然我们可以通过 props 传递插值的方式来实现,但是对于某个只需要重载一次的样式来说这样做的成本还是有点高.
  • 此时可以创建一个继承其它组件样式的新组件,最简单的方式就是用**构造函数styled()**包裹被继承的组件.
  • 下面的示例就是通过继承上一节创建的按钮从而实现一些颜色相关样式的扩展:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // The Button from the last section without the interpolations
    const Button = styled.button`
    color: palevioletred;
    font-size: 1em;
    margin: 1em;
    padding: 0.25em 1em;
    border: 2px solid palevioletred;
    border-radius: 3px;
    `;

    // A new component based on Button, but with some override styles
    const TomatoButton = styled(Button)`
    color: tomato;
    border-color: tomato;
    `;

    render(
    <div>
    <Button>Normal Button</Button>
    <TomatoButton>Tomato Button</TomatoButton>
    </div>
    );
    新的TomatoButton仍然和我们自定义的Button类似,我们只是添加了两条规则:
    效果

as动态修改元素

  • 使用as可使一个样式组件依赖于不同的元素。
  • 在某些情况下,可能希望更改 自定义样式组件 所依赖的元素
  • 比如,构建导航栏时,有链接和按钮的混合,但是它们的样式应该是相同的
  • 那么如何使用相同的样式组件代表不同的元素呢?可以使用as,as可以动态地交换出元素,接收你写的样式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    const Button = styled.button`
    display: inline-block;
    color: palevioletred;
    font-size: 1em;
    margin: 1em;
    padding: 0.25em 1em;
    border: 2px solid palevioletred;
    border-radius: 3px;
    display: block;
    `;

    const TomatoButton = styled(Button)`
    color: tomato;
    border-color: tomato;
    `;

    render(
    <div>
    <Button>Normal Button</Button>
    <Button as="a" href="/">Link with Button styles</Button>
    <TomatoButton as="a" href="/">Link with Tomato Button styles</TomatoButton>
    </div>
    );
    效果

as也可让样式组件用于修饰样式组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Button = styled.button`
display: inline-block;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
display: block;
`;

const ReversedButton = props => <Button {...props} children={props.children.split('').reverse()} />

render(
<div>
<Button>Normal Button</Button>
<Button as={ReversedButton}>Custom Button with Normal Button styles</Button>
</div>
);

效果


&伪元素,伪类选择器,组合选择器和嵌套

  • styled-component使用的预处理程序stylis 支持类似于scss的语法来自动嵌套样式
  • 与号&可以用来引用主组件
  • 下面是一些关于它潜在用法的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
    color: blue;

    // <Thing> 的 伪类选择器,当鼠标移上时字变红
    &:hover {
    color: red;
    }

    //<Thing> 的 所有同级选择器,背景色变红
    & ~ & {
    background: tomato;
    }

    //<Thing> 的 直接相邻的同级选择器,背景变绿
    & + & {
    background: lime;
    }

    //拥有className为something的<Thing> (注意这里没有空格)
    &.something {
    background: orange;
    }

    // <Thing>在另一个标签为".something-else"的元素中
    .something-else & {
    border: 1px solid;
    }
    `

    render(
    <React.Fragment>
    <Thing>Hello world!</Thing>
    <Thing>How ya doing?</Thing>
    <Thing className="something">The sun is shining...</Thing>
    <div>Pretty nice day today.</div>
    <Thing>Don't you think?</Thing>
    <div className="something-else">
    <Thing>Splendid.</Thing>
    </div>
    </React.Fragment>
    )
    效果

如果只写选择器而不带&,则指向组件的子节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Thing = styled.div`
color: blue;

//<Thing>的子元素中带有className为something的子元素
.something {
border: 1px solid;
display: block;
}
`

render(
<Thing>
<label htmlFor="foo-button" className="something">Mystery button</label>
<button id="foo-button">What do I do?</button>
</Thing>
)

效果


attrs给组件附加额外的属性

  • 可参考官方文档

  • 比如用于在style.js中给a标签添加href属性const Logo = styled.a.attrs({href:/}),**相当于在index.js中的<Logo href="/" />**。

  • 为了避免仅为传递一些props来渲染组件或元素而使用不必要的wrapper, 可以使用 .attrs ,通过.attrs可以添加额外的 props 或 attributes 到组件

    • 注意:不一定是新属性,也可以使用.attrs给组件设置原生的属性,比如inputplaceholder或者type
  • attrs是一种可链接的方法,可通过attrs将 静态属性/动态属性 附加到样式化的组件上。attrs接受的唯一参数是一个对象/函数,对象/函数的返回值 将被合并到组件的其余props中

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const Input = styled.input.attrs(props => ({
// 静态属性
type: "password",

// 动态属性,存在size则用size
size: props.size || "1em",
}))`
//注意,这里开始才设置样式
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;

/* here we use the dynamically computed prop */
margin: ${props => props.size};
padding: ${props => props.size};
`;

render(
<div>
<Input placeholder="A small text input" size="1em" />
<br />
<Input placeholder="A bigger text input" size="2em" />
</div>
);

结果


动画

  • 虽然使用@keyframes的 CSS 动画不限于单个组件,但我们仍希望它们不是全局的(以避免冲突). 这就是为什么 styled-components 导出 keyframes helper 的原因: 它将生成一个可以在 APP 应用的唯一实例.
  • 可参考官方文档

用于React-Native

  • styled-components 可以在 React-Native 中以同样的方式使用
  • 注意:react-native不支持 keyframes.
  • 可参考官方文档
,