你不知道的react插件之styled-components

4,390 阅读6分钟

我为什么要使用styled-components

最近一直在学习和使用react开发自己的博客系统,可能是之前一直都在用vue的缘故,在学习过程中脑子里面一直都潜移默化的和vue比较,其中让我最难受的就是react中css的书写。

因为在vue中每个组件都有单独写style的地方,并且都相互不影响,这样更符合组件化和减少多人开发中的样式冲突。但到react中我也尝试把样式和逻辑代码放到一起,这样可避免多人开发合并代码后带来的冲突,出bug了也好定位到个人,但是在js文件里面写css真的很难受,有些地方必须要加引号,比如:

const style = {
    'color': 'red',
    'fontSize': '79px' 
};

const clickWorld = () => alert('hell0');

ReactDOM.render(
    <h2 style={style} onclick={clickWorld}>
        Hello, world!
    </h2>,
    document.getElementById('example')
);

这样导致书写很慢,出问题后在js文件里面找css的问题也不一目了然,如果每个组件都写一个css文件然后单独引入的话会使项目变得很冗杂。总之之前的css书写不舒服,维护也不方便。但是直到我遇到styled-components后浑身就舒畅了。

什么是styled-components

styled-components是针对react的一套css in js框架,什么意思呢?就是让你在js中写css,并且和在css文件中一样书写流畅,而且用的是js语法,没啥学习成本。如果你喜欢Vue中书写css的方式,那么这个插件你肯定相见恨晚。夸了这么多它的好是不是有点抽象,下面来举个栗子吧。

  • class类名唯一

    当你使用它之后,打开控制台你就会发现这样的情况

    styled1

    天啊,我的类名咋成乱码了?不要慌,这是styled-components自己处理的,为了保证你定义的每个组件样式唯一,妈妈再也不用担心我为组件起名了,而且开发过程中起名在你不经意间就会重复,这样带来的后果肯定大部分人都体会过。

  • 以样式为结构

    可以以样式为结构,比如我先定义一个背景为红色,宽高为200px的div,div里面span字体大小为13px,颜色为蓝色。这时你把组件放进去,这个组件里面的div就是红色、宽高200px,里面的span的字体也就是蓝色、13px。

    const styleContainer = styled.div`
    height:200px;
    width:200px;
    background-color:red;
    span{
     font-size:13px;
     color:blue;
    }
    `
    const container = () =>(
     <styleContainer>
        <div>
         <span>油炸日子</span>
        </div>
      </styleContainer>
      )
    
    

    这样写起来样式是不是很好维护,哪个组价出问题了,直接找样式结构就可以啦。

styled-components的使用

  • 安装

    npm install --save styled-components
    
    yarn add styled-components
    

    如果你的编辑器是vscode的话建议你下载vscode-styled-components这个插件,它让智能提示失而复得。

  • 入门

    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = styled.div`
    background-color:red;
    span{
      color:blue
    }
    `
    const Layout = () => (
      <UzrzContainer>
        <div>
          <span>油炸日子</span>
        </div>
      </UzrzContainer>
    )
    export default Layout;
    
  • 传递props

    1. 样式组件可以透传包裹的组件的props
    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = styled.input`
    `
    const Layout = () => (
      <UzrzContainer type="password" placeholder="油炸日子">
      </UzrzContainer>
    )
    
    export default Layout;
    
    

    type和placeholder属性是input标签的属性,但是样式组件可以把属性透传到包裹的input上

    1. 可以通过react组件传递porps来动态改变样式
    const UzrzContainer = styled.div`
    background-color:red;
    span{
      color:blue;
       font-size:${props => props.size};
    }
    `
    const Layout = () => (
      <UzrzContainer size="20px">
        <div>
          <span>油炸日子</span>
        </div>
      </UzrzContainer>
    )
    
    1. 可以通过传递的props做样式判断
    const UzrzContainer = styled.div`
    background-color:red;
    span{
      color:blue;
       font-size:${props => props.size ? props.size : '14px'};
    }
    `
    const Layout = () => (
      <UzrzContainer size>
        <div>
          <span>油炸日子</span>
        </div>
      </UzrzContainer>
    )
    
  • extend 继承

    有些时候我们需要设计一套组件,比如设计一套按钮,这一套按钮只是背景颜色不变,其他的样式都一样,那么这时就是extend大放异彩的时候了。

    cosnt UzrzPrimaryButton = styled.button`
      background-color:blue;
      width:200px;
      height:50px;
      border-radius:10px;
    `
    // 这里只需要让UzrzWarningButton继承UzrzPrimaryButton的样式,然后设置自己的background-color,然后UzrzPrimaryButton的背景颜色就会被UzrzWarningButton替换掉。
    const UzrzWarningButton = styled(UzrzPrimaryButton)`
     background-color:yellow
    `
    
  • 样式化组件

    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = (props) => (<div>
      <span className={props.className}>{props.children}</span>
    </div>
    )
    
    const CopyContainer = styled(UzrzContainer)`
    background-color:red;
    span{
      color:blue;
    font-weight:bold;
    }
    
    `
    
    const Layout = () => (
      <div>
        <UzrzContainer >油炸日子</UzrzContainer >
        <CopyContainer>油炸日子</CopyContainer>
      </div>
    )
    export default Layout;
    

    **注意:**这里的className一定要加上,否则样式并没有生效。

    这里相当于CopyContainer复制了UzrzContainer这个组件,然后装饰了下。

    假如你想复制这个组件并且放到另外一个容器里面,你还可以component selector样式化组件,这样是不是少写了很多class,命名有些时候蛮痛苦的,但是styled-components解决的挺干净的。

    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = (props) => (<div>
      <span className={props.className}>{props.children}</span>
    </div>
    )
    
    const CopyContainerInner = styled(UzrzContainer)``
    const CopyContainerOut = styled.div`
     padding:20px;
     ${CopyContainerInner}{
        background-color:red;
        color:#fff;
     }
    `
    const Layout = () => (
      <div>
        <CopyContainerOut >
          <CopyContainerInner>油炸日子</CopyContainerInner>
        </CopyContainerOut >
    
      </div>
    )
    export default Layout;
    
  • withComponent

    有些时候我在样式写好了后,可能想想更改组件的标签类型,这时候可以用下withComponent

    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = styled.div`
    background-color:red;
    color:blue;
    `
    const CopyContainer = UzrzContainer.withComponent('span')
    
    const Layout = () => (
      <div>
        <UzrzContainer >油炸日子</UzrzContainer >
        <CopyContainer>油炸日子</CopyContainer>
      </div>
    )
    export default Layout;
    

    当我们审查元素时就会发现UzrzContainer渲染的元素是div,CopyContainer渲染出来的是 span。

  • refs

    这里使用的4.0.0及以上的版本,如果你使用的是4.0.0以下的版本,那么获取dom就要使用innerRef,这里只是介绍refs,innerRef请移步官网styled-components

    4.0.0版本更新后在styled组件中获取dom和在react组件获取dom的操作是一样的,也是无缝连接。

    import React, { Component } from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = styled.input`
     background-color:red;
     color:blue;
     width:200px;
     height:50px;
     `
    class Layout extends Component {
      constructor(props) {
        super(props);
        this.state = {}
        this.input = React.createRef()
      }
      render() {
        return (<UzrzContainer ref={this.input} onMouseEnter={() => { console.log(this.input) }}>
        </UzrzContainer >);
      }
    }
    
    export default Layout;
    
  • isStyledComponent

    有些时候我们需要判断一个组件是不是StyledComponent,我们才好运用只有StyledComponent才具有的特性,比如样式化组件

    import React from 'react';
    import styled, { isStyledComponent } from 'styled-components';
    import OuterComponents from './outer'
    
    let UzrzComponent =
      isStyledComponent(OuterComponents)
        ? OuterComponents
        : styled(OuterComponents)``;
     
    const UzrzContainer = styled.div`
      color: blue;
      ${UzrzComponent} {
        color: red;
      }
    `
    const Layout = () = (
     <UzrzContainer>
      <UzrzComponent />
     </UzrzContainer>
    )
    
    
  • attr

    给样式组件添加默认属性

    import React from 'react';
    import styled from 'styled-components'
    
    const UzrzContainer = styled.div.attrs({
      // 给盒子添加css属性,赋予初始值
      margin: props => props.out || '0',
      padding: props => props.inner || '0'
    })`
     margin:${props => props.margin};
     padding:${props => props.margin};
     `
    
    const Layout = () => (
      <UzrzContainer out="20px" inner="30px">
        油炸日子
      </UzrzContainer>
    )
    
    export default Layout;
    
  • 动画

    官网上面解释的意思大概是:带有@keyframes的CSS animations,一般来说会产生复用。styled-components暴露了一个keyframes的API,我们使用它产生一个可以复用的变量。这样,我们在书写css样式的时候使用JavaScript的功能,为CSS附能,并且避免了名称冲突。

    import React from 'react';
    import styled, { keyframes } from 'styled-components'
    
    const scaleTwo = keyframes`
    from{
      transform:scale(1)
    }
    to{
      transform:scale(2)
    }
    `
    const Uzrz = styled.div`
     animation: ${scaleTwo} 2s linear infinite;
     background-color:red;
    `
    const Layout = () => (
      <Uzrz >油炸日子</Uzrz>
    )
    
    export default Layout;
    
  • 全局样式

    Styles-components也提供了全局样式的函数createGlobalStyle

    import { createGlobalStyle } from 'styled-components'
    const GlobalStyle = createGlobalStyle`
     body{
       margin:0;
       padding:0;
       width:100vw;
       height:100vh;
     }
    `
    export default GlobalStyle
    
  • 主题

    styled-components还提供了一个ThemeProvider容器组件,这样你可以用来设置默认主题和更好地切换主题。

    下面我们来完成一个切换主题的功能

    import React from 'react';
    import styled, { ThemeProvider } from 'styled-components'
    
    const defaultTheme = {
      backgroundColor: "white",
      color: '#000'
    }
    
    const darkTheme = {
      backgroundColor: "black",
      color: '#fff'
    }
    const Uzrz = styled.div`
     background-color:${props => props.theme.backgroundColor};
     color:${props => props.theme.color};
     button{
       background-color:${props => props.theme.backgroundColor};
       color: ${ props => props.theme.color};
     }
    `
    class Layout extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          selectTheme: defaultTheme //给组件一个默认的初始主题
        }
        this.changeTheme = this.changeTheme.bind(this)
      }
      changeTheme() {
        // 点击切换主题按钮,把没有选中的主题赋值给selectTheme
        this.state.selectTheme === defaultTheme ? this.setState({
          selectTheme: darkTheme
        }) : this.setState({
          selectTheme: defaultTheme
        })
      }
      render() {
        return (
          <ThemeProvider theme={this.state.selectTheme}>
            <Uzrz>
              <button onClick={this.changeTheme}>切换主题</button>
            </Uzrz>
          </ThemeProvider>
    
        );
      }
    }
    
    export default Layout;
    
    

    这样写主题是不是很方便呢。

  • 最后

    Styled-components也可以用在react native上,并且对SSR服务端渲染还有很好地支持等等一些很不错的功能,但是小编暂时还没有用到,等以后使用后会慢慢补充的。谢谢小伙伴们花时间和我一起学习~