team icon indicating copy to clipboard operation
team copied to clipboard

styled-components 样式组件

Open MayOMengxuhui opened this issue 6 years ago • 1 comments

styled-components

背景

  • 使用React的过程中,组件通过 JS 来定义 css 样式,就必须在 React 组件里面通过style属性来写。
 export class App extends React.Component {
    render() {
        return(
            <div style={{marginTop: '10px'}}>
            	......
            </div>
        );
    }
}

缺点:

(1)样式属性与原生 css 的写法不一致,样式名称必须用驼峰的命名方式来命令,而不是多个单词用-号分隔;单位的写法也不一致,百分比是用字符串形式。 (2)无法写伪元素,要实现hover的话就必须使用onMouseOver事件来实现,而且要写很多 JS 代码,像before和after这类伪元素则根本没法通过这种方式实现。

为了顺应组件化的潮流,人们开始考虑使用JS上编写CSS,styled components就是其中一种解决方案。styled components是一个React第三方库,作用是可以将样式写成组件的形式,实现在JS上编写CSS

基本用法

  • 使用styled-components不需要再使用className属性来控制样式,而是将样式写成更具语义化的组件的形式,如下例:
import React from 'react';
import styled from 'styled-components'; 
 
const Title = styled.h1`
    font-size: 1.5em;
    text-align: center;
    color: red;
`;
 
class App extends React.Component {
    render() {
        return (
            <Title>Hello styled--components</Title>
        )
    }
}
 
ReactDOM.render(
    <App />,
    document.getElementById('app')
);

示例: 1522579578467 上例中的styled.h1是一个标签模板函数,紧跟其后的是一个模板字符串参数,“标签模板”和“模板字符串”都是es6的语法。styled.h1函数返回一个React Component,styled components会为这个React Component添加一个class,该class的值为一个随机字符串。传给styled.h1的模板字符串参数的值实际上是CSS语法,这些CSS会附加到该React Component的class中,从而为React Component添加样式。

1522581565973

props参数控制样式

  • 在React中是通过控制className和style来控制样式的,如下例,App组件需要判断是否有primary属性来判断是否渲染”title-primary”类的样式:
export class App extends React.Component {
   render() {
       let className = this.props.primary ? "title-primary" : '';
       return(
           <div className={className}>
               ...
           </div>
       );
   }
}

styled components使用props来控制样式,将控制样式代码放在样式组件中,使React组件更加简洁:

const Title = styled.div`
   font-size: 1.5em;
   text-align: center;
  color: ${props => props.primary ? 'blue' : ' red'};
`
export class App extends React.Component {
   render() {
       return (
           <Title primary>
               Hello Styled-Component
           </Title>
       )
   }
}

1522582460455 styled components的写法中样式组件暴露props让外层JS来控制样式,不再需要className或style这样的“中间人”,移除了样式和组件间的映射关系。

CSS作用域

  • CSS有一个痛点——CSS的作用域是全局的。当两个CSS选择器有冲突时,会根据选择器的权值确定使用哪一个选择选择器。当项目较大时,编写的css选择器很难判断会不会把另外一个选择器冲掉。 解决CSS作用域的其中一个方法就是使用后代选择器,这种用法和命名空间相似,即在每个选择器前添加一个父元素的选择器,从而减少选择器冲突的概率。 使用styled components会给生成的React组件添加一个值为随机字符串的className。使用同一个styled components生成的多个React组件的className是不同的,这种随机className的机制使得组件之间的className值不会冲突,从而解决了CSS全局作用域的问题。

styled components也支持css的嵌套语法:

const Title = styled.div`
   font-size: 1.5em;
   text-align: center;
   .content1 {
          color: #f16da7;
          margin: 10px;
    }
    .content2 {
        color: #98a514;
    }
`;

export class App extends React.Component {
   render() {
       return (
           <Title>
               <div className="content1">content1-style</div>
               <div className="content2">content2-style</div>
           </Title>
       )
   }
}

示例 1522584049702

组件样式继承

  • 通常在 css 中一般会通过给 class 传入多个 name 通过空格分隔的方式来复用 class 定义,类似 class="button tomato"。在 styled-components 中利用了 js 的继承实现了这种样式的复用:
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;

const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;

export class App extends React.Component {
  render() {
      return (
          <React.Fragment>
          <Button>
              <TomatoButton>styled-components</TomatoButton>
          </Button>
      </React.Fragment>
      )
  }
}

示例 1522584610585 1522584703345

我们可以看到在原生的样式和组件之间是多对多的映射关系,styled-components 通过继承彻底的移除了这种只为了样式而存在映射关系。组件内部会执行类似于对象合并的操作,子组件中的属性会覆盖父组件中同名的属性。

styled-components中的 attrs

styled-components 同时支持为组件传入 html 元素的其他属性,比如为 input 元素指定一个 type 属性,我们可以使用 attrs 方法来完成:

const Password = styled.input.attrs({
    type: 'password'
})`
    border: 1px solid #ccc;
    padding: 7px 0px;
    border-radius: 3px;
    padding-left:5px;
    -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
    box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
    -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
    -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
    transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
`;

export class App extends React.Component {
    render() {
        return (
            <React.Fragment>
            <Password name="pwd"/>
        </React.Fragment>
        )
    }
}

示例 1522585836037 在实际开发中,这个方法还有一个有用处,用来引用第三方类库的 css 样式和自己定义的类名:

const Row = styled.div.attrs({className: "row"})`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
`
export class App extends React.Component {
    render() {
        return (
            <React.Fragment>
            <Row>
                hello styled-components
            </Row>
        </React.Fragment>
        )
    }
}

示例 1522586415021

theme 特性

  • styled-components 里面还有一个比较特别的特性是theme,它利用了 React 的context特性来传递其中的参数,可以让样式对象直接从最上层的组件传递到最下层的组件。
const Box = styled.div`
 color: ${props => props.theme.color};
`;
export class App extends React.Component {
   render() {
       return (
           <React.Fragment>
               <ThemeProvider theme={{ color: 'mediumseagreen' }}>
                   <Box>sI'm mediumseagreen!</Box>
               </ThemeProvider>
       </React.Fragment>
       )
   }
}

示例 1522587877797

独立样式文件

在一般的 React 组件中,样式和组件文件是分离的(比如 App.js 和 style.css),使用了 styled-components 后其实也可以沿用这种方式来组织文件,定义一个style.js文件来写样式对象,然后在组件文件中引入该样式文件。

// style.js
import styled from 'styled-components';

export const AppDiv = styled.div`
 color: palevioletred;
 font-size: 1em;
 margin: 1em;
`;

// App.js
import React from 'react';
import * as css from './style';

export class App extends React.Component {
   render() {
       return (
           <React.Fragment>
               <css.AppDiv>Hello styled-components</css.AppDiv>
       </React.Fragment>
       )
   }
}

总结

  1. 可以在样式定义中直接引用到 js 变量,共享变量,非常便利;
  2. 支持组件之间继承,方便代码复用,提升可维护性;
  3. 兼容现有的 className 方式,升级无痛;

参考链接: https://www.styled-components.com/

MayOMengxuhui avatar Apr 01 '18 13:04 MayOMengxuhui

这篇文章有点老了,现在已经不使用 .extend 这个方法了。

kmvan avatar Sep 29 '19 01:09 kmvan