前言

React 是一个构建用户界面的 js 库,从UI=render()这个等式中就很好的映射了这一点,UI的显示取决于等式左边的render函数的返回值.

而编写React利用,就是在编写React组件,组件中最重要的数据就是propsstate,有了数据,怎么让其以什么样的显示,那就是 CSS 做的事件了

React中,所有皆能够是Js,也就是说在js外面能够写css,这相比传统的内容(html),层叠款式(css),行为动作(js)进行拆散,这种拆散仅仅是把三个不同的技术进行了物理上的拆散,进行离开治理,如果从另一个视觉角度上讲,并没有实现高内聚的特点

既然前端自身就是页面的展现,那么把jscss放在一起,也是一种细粒度的组合,css也能够和Js一样,通过模块化的模式嵌入到 js 外面去

CSS modules很好的解决了款式抵触,利用了分而治之的理念,在现在组件化开发大行其道上,同样 css 也在一直的进化,如同 js 一样,也有变量,函数等具备Js一样的生机,那么在React中是怎么实现款式的模块化的?

  • 通过独自的*.css文件定义组件的款式,并且通过clssName指定他们,有什么不好的?
  • JSX上进行事件的监听绑定,通过on*EventType只针对原生的HTML标签起作用的,如果是自定义的组件,是不起作用的,有什么好的解决办法?
  • 款式化组件的魅力(特点)

那么本节就是你想要晓得的

如果浏览体验更好能够返回React中编写款式(styled-components)

React 中组件模式

对于React中定义组件的模式,有如下几种形式,其中前两个在之前的学习当中,置信你曾经很相熟了的,如果不分明,能够查看后面的内容的

  • class申明的组件(类组件/容器组件)
  • 函数式申明的组件(函数组件/无状态组件/UI组件)
  • 款式化组件(styled-components)

本节次要讲的就是款式化组件,给一个 React 组件增加款式,有哪些形式?上面一起来看看的

行内款式 VS 内部款式

想要给React组件增加款式,常见的形式有

  • JSX上增加style属性定义行内款式
  • 通过import关键字引入内部款式

像如下所示,在JSX上增加款式: 上面的代码是用class类组件申明了一个Header组件,这个组件返回了一个button按钮,给这个按钮通过style增加了一些款式

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';class Header extends Component { render() { return ( <Fragment> <button style={{ width: '100px', height: '40px', borderRadius: '3px', outline: 'none', outline: 'none', border: 'none', cursor: 'pointer', background: '#abcdef', color: '#fff', }} > button按钮 </button> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Header />, container);

对于下面的行内款式,也能够把它定义成一个对象 Object 的形式去定义款式,与上面是等价的

class Header extends Component { render() { const btnStyle = { width: '100px', height: '40px', borderRadius: '3px', outline: 'none', border: 'none', cursor: 'pointer', background: '#abcdef', color: '#fff', }; return ( <Fragment> <button style={btnStyle}>button按钮</button> </Fragment> ); }}

尽管这样也是在JS中写css款式,然而治理起来并不不便,很多时候,咱们是用clssName的形式去定义款式的 ,依照咱们相熟的形式,就是把款式文件命名成*.css,而后通过import的形式给引入进去

import './style.css';

对于款式名,有时候,对于各个不同的组件的className有可能会一样,如果是这样的话,前面引入的款式名会笼罩后面的,这样的话显然不是咱们想要的后果了

那有什么好的解决办法?

React中有css-in-js,它是一种模式,这个cssjs生成而不是在内部文件中定义,是CSS Modules,次要是借助第三方库生成随机类名称的形式来建设一种部分类名的形式

这种css-in-js的第三方模块有很多:能够拜访:https://github.com/MicheleBertoli/css-in-js

明天的次要学习的是githubstar数最多的,styled-components

应用styled-components的益处是:它能够让组件本人的款式对本人失效,不是全局失效,做到互不烦扰

首先你得通过npm或者cnpm进行装置styled-components模块

npm install -S styed-components

在装置完后,在应用styled-components的文件内,通过import的形式引入该模块

如下代码所示: 在文件的上方引入styled-components,实例化了一个styled对象,通过给styled对象下增加你想要的 html 元素,利用了Es6中的一个模板字符串,反引号

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import styled from 'styled-components'; // 引入styled-components库,实例化styled对象// 申明款式ButtonA组件,通过styled对象进行创立,留神styled.html元素,前面是反引号const ButtonA = styled.button` width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer; background: #abcdef; color: #fff;`;// 款式化申明ButtonB组件const ButtonB = styled.button` background: red; color: #fff; width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer;`;class Header extends Component { render() { return ( <Fragment> <ButtonA>按钮A</ButtonA> <ButtonB>按钮B</ButtonB> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Header />, container);

这是渲染的后果:

留神:要防止在render办法中申明款式化组件 如下所示:这样程序尽管不报错,然而会引起性能问题,引起组件不必要的渲染

上面这种做法是不举荐的,该当防止应用

class Header extends Component { render() { const ButtonA = styled.button` width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer; background: #abcdef; color: #fff; `; // 申明款式ButtonB组件,不应该在render函数外面申明款式组件 const ButtonB = styled(ButtonA)` background: red; `; return ( <Fragment> <ButtonA>按钮A</ButtonA> <ButtonB>按钮B</ButtonB> </Fragment> ); }}

因为在render办法中申明款式化组件,每次都会动静渲染创立一个新的组件。这意味着React必须在每个后续渲染中抛弃并从新计算DOM树的那局部,而不是仅计算它们之间发生变化的差别。这会导致性能瓶颈

正确的做法就是如同刚开始那样,把款式组件放到组件最里面去

当然,为了便于款式的集中管理,对于款式化组件,咱们往往会把它写在一个文件中去,把下面的款式组件放到一个style.js的文件中去,而后通过Es6中模块化的export的形式对外裸露进来,只有哪个组件模块须要,间接通过import引入即可

import styled from 'styled-components'; // 引入styled-components// 申明款式ButtonA组件const ButtonA = styled.button` width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer; background: #abcdef; color: #fff;`;// 申明款式ButtonB组件const ButtonB = styled.button` background: red; color: #fff; width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer;`;// 对外裸露进来export { ButtonA, ButtonB };

仔细的敌人你会发现,其实两个按钮有很多雷同的款式,只有背景色彩不一样而已,如果反复写很多款式,那么必定是有很多冗余的代码,styled-components中提供了继承的能力

要创立一个继承另一个款式的新组件,只需将其包装在styled(继承的组件)构造函数中即可,如下所示

// 申明款式ButtonA组件const ButtonA = styled.button` width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer; background: #abcdef; color: #fff;`;// 申明款式ButtonB组件,同时款式ButtonB组件继承了ButtonA组件的款式,又给本身拓展了款式,更改了本身的背景色const ButtonB = styled(ButtonA)` background: red;`;

在要应用款式组件的中央通过import引入ButtonA,ButtonB组件

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import { ButtonA, ButtonB } from './style';class Header extends Component { render() { return ( <Fragment> <ButtonA>按钮A</ButtonA> <ButtonB>按钮B</ButtonB> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Header />, container);
款式组件能够接管 props

对于组件内部定义的属性,在款式组件内能够进行接管,写一些简略的逻辑表达式 如下所示:在确定按钮组件内设置了一个color属性,在款式组件内能够通过props进行接管

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import { Button } from './style';class Header extends Component { render() { return ( <Fragment> <Button>勾销</Button> <Button color="red">确定</Button> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Header />, container);

在款式组件内,属性值能够通过Es6中的插值表达式,${表达式}的形式进行指定的逻辑判断,达到本人想要的目标

import styled from 'styled-components'; // 引入styled-components// 申明款式Button组件const Button = styled.button` width: 100px; height: 40px; border-radius: 3px; outline: none; border: none; cursor: pointer; background: ${(props) => (props.color ? 'red' : '#fff')}; color: ${(props) => (props.color ? '#fff' : 'red')}; border: 2px solid red; margin-right: 15px;`;// 对外裸露进来export { Button };

渲染的后果如下所示

这里只是为了阐明在款式化组件外部能够接管 props 值,有时候,在一些场景下是很有用的

例如:本人封装一些本人组件,不同大小按钮等等的,通过在组件内部设置属性值,而后在款式组件外部进行接管,管制组件的款式 UI 状态

当然这种简略的款式解决,齐全是能够用下面继承的形式去解决的

值得注意的是,在插入背景图片时,是不反对直接插入的,这样是不失效的

const Content = styled.div` width: 550px; height: 290px; background: url('./react.jpg');`;

引入本地图片必须得通过定义变量的形式来实现,如下所示

import BgImg from './react.jpg'; // 将图片定义成一个变量的形式来援用const Content = styled.div` width: 550px; height: 290px; background: url(${BgImg}); // 留神这里用Es6中的模板语法`;
 .attrs办法反对给组件增加属性

attrs是一个构造方法,能够给款式组件增加本身的额定属性(这个属性只容许html标签原生自有的属性),不反对自定义属性,要想增加自定义属性,只能在 jsx 元素上进行增加

attrs可接管两种类型的参数:

  • 参数能够接管一个对象,通过它增加的属性,会被合并到款式组件当中去
  • 参数能够是一个函数,如果有props值,则可应用该模式 如下代码所示
import styled from 'styled-components'; // 引入styled-components// 参数是一个对象const Input = styled.input.attrs({ placeholder: '请输出信息', type: 'text',})` width: ${(props) => props.width}; height: ${(props) => (props.size === 'small' ? '24px' : '40px')}; font-size: 14px; text-indent: 10px; border-radius: 3px; border: 1px solid palevioletred; display: block; margin: 0 0 1em; ::placeholder { color: palevioletred; }`;// 对外裸露进来export { Input };

在要援用组件处

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import { Input } from './style';class Header extends Component { render() { return ( <Fragment> <Input width="150px" placeholder="请输出账号" size="small" /> <Input width="200px" placeholder="请输出明码" size="large" type="password" /> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Header />, container);

渲染后果如下所示:

如果有参数能够将款式组件写成上面这样,attrs内可接管一个函数,并且通过props接管内部的属性值

const Input = styled.input.attrs((props) => ({ // 参数是一个函数,能够通过props进行接管 placeholder: '请输出信息', type: 'text',}))` width: ${(props) => props.width}; height: ${(props) => (props.size === 'small' ? '24px' : '40px')}; // 如下省略`;

留神:对于款式的优先级

行内款式>内部款式(款式组件),如果行内元素设置的了默认值,则行内款式优先

否则,在attrs内设置的属性会笼罩内部的属性

至于什么时候用attrs

应用attrs将属性传递给款式化组件

当你心愿款式化组件的每个实例都具备该prop时应用attrs,换句话说,通过attrs设置的属性,它是公共的,如果每个实例须要不同的实例时则可间接传递props

 如何笼罩默认款式

有时候,须要笼罩款式最粗鲁的形式就是在属性前面加权重,通过!important来实现,但这很容易出错,并且很容易出问题

具体的实现形式是通过&符号的形式,每增加一个&符号,都会随机生成一个类款式

const ButtonB = styled(ButtonA)` &&& { color: palevioletred; font-weight: bold; }`;

如下图所示

如何笼罩内联款式

内联款式的优先级是最高的,始终优先于内部CSS,因而无奈通过简略地款式组件笼罩它,然而有具体的解决办法的, 就是应用&[style]!important加权重的形式

有时候,如果在JSX上申明了行内款式,然而内部想要笼罩它,那么这个时候,&[style]import加权重的形式就很有用了的,然而在理论开发中,应该防止应用行内款式,在这里只是为了阐明诸如此类的解决办法

const ButtonB = styled(ButtonA)` &[style] { background: blue !important; font-weight: bold; }`;

同样,每追加一个&符号,都会新增加一个类,在理论的我的项目中,该当少用行内款式的,不要一时爽,前面给本人挖坑的

 重置全局款式

对于React中重置默认款式,它应用的是createGlobalStyle这个函数,须要从styled-components中注入 如下所示:

import { createGlobalStyle } from 'styled-components';const globalStyle = createGlobalStyle` html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } body { margin: 0; padding: 0; font-family: sans-serif; }`;export default globalStyle;

一般而言,把重置款式独自的定义成一个文件,独自的引入到index.js当中去的,全局失效的

须要留神的是:在新近的版本中应用全局的形式是injectGlobal,而这个API曾经废除,并由styled-components v4中的createGlobalStyle替换了

CSS-module 编写款式

在应用create-react-app脚手架创立的我的项目后,该我的项目是反对css-module

然而须要留神以下几点:

  • 款式文件的名称必须以xxx.module.css或者xxx.module.scss的模式命名:例如styles.module.css或者styles.module.scss
  • 以变量的模式导入款式文件,比方:import styles from './style.module.css',如果是间接导入xxx.css,在JSX元素上的className的属性名称,是无奈通过变量对象引入款式的,如果是间接引入款式,则在className的属性值中间接引入类名即可

    className以变量援用的形式进行增加,例如:className ={styles.counter}

  • 应用sass时,脚手架创立的我的项目,默认是反对sass的,应用时只须要装置一下node-sass这个包即可

在根文件夹下定义styles.module.css文件,写入如下几行款式代码

.counter{ text-align: center;}.button{ padding: 0 10px;}.spanText{ padding: 0 15px;}

在应用css-module款式的文件内,通过import的形式引入该xxx.module.css文件

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import styles from './styles.module.css'; // 引入styles.module.css,实例化一个styles对象class Counter extends Component { constructor(props) { super(props); this.state = { count: 0, }; } increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <Fragment> <div className={styles.counter}> <button className={styles.button} onClick={this.decrement}> - </button> <span className={styles.spanText}>{this.state.count}</span> <button className={styles.button} onClick={this.increment}> + </button> </div> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Counter />, container);

具体成果如下所示

<div align="center">

<img class="medium-zoom lazy" loading="lazy" src ="../images/framework-article-imgs/base-authoring-styled-components/01-add.gif" alt="JSX" />

</div>

对于以上的写法,是咱们在React中常见的写法,然而如果用styled-components的形式,那又该怎么样?如下代码所示

import React, { Fragment, Component } from 'react';import ReactDOM from 'react-dom';import styled from 'styled-components'; // 引入styled-components// 在Render函数外定义款式组件const CounterWrap = styled.div` text-align: center;`;const SpanText = styled.span` padding: 0 15px;`;const Button = styled.button` padding: 0 10px;`;class Counter extends Component { constructor(props) { super(props); this.state = { count: 0, }; } increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <Fragment> <CounterWrap> <Button onClick={this.decrement}>-</Button> <SpanText>{this.state.count}</SpanText> <Button onClick={this.increment}>+</Button> </CounterWrap> </Fragment> ); }}const container = document.getElementById('root');ReactDOM.render(<Counter />, container);

当然你能够跟之前一样,把款式组件独自的抽离进来的,而后通过export对外裸露进来的,当须要应用时,在另一文件内通过import引入即可

对于款式组件的命名:因为是组件,所以约定俗成的首字母大写,这是为了辨别一般的 html 标签元素

小 tip:在vs-code举荐插件:vscode-styled-components

上面来总结一些styled-components的一些个性

 styled-components 反对的个性
  • 反对嵌套,变量和继承:能够应用相似sass,less的语法嵌套,能够应用变量来设置不同的款式,应用这些不同款式时只须要给款式组件传递一个参数就能够了的,在款式化组件外部能够通过props来接管内部的的参数值
  • 事件监听绑定:对于自定义的款式化组件能够进行事件监听的绑定,这正是解决类 class 申明的自定义组件,无奈绑定事件监听的痛点,onEventType事件类型只针对原生HTML标签才起作用,而款式化组件正好补救了这一点
  • 模块化css:按需引入组件的代码,防止了一些多余的代码
  • 惟一类名,没有类名谬误,反复:styled-components生成的款式生成惟一的类名。永远不用放心反复,重叠或拼写错误
  • 更容易的删除款式,保护简略:编写的款式都与特定组件相关联,如果组件未应用(工具能够检测到)并被删除,则所有款式都将被删除,放弃功能性的繁多,达到了高内聚,低耦合的组件化特点
  • 动静款式:款式组件内能够接管参数,很简略地调整和拓展组件的款式,而不须要建设很多个 class 类来保护组件的款式
结语

本文次要解说了React编写款式的姿态,并不是什么高大上的内容,比拟根底

通过styled-components第三方库的的引入,编写款式化组件,这个并不是必须的,视我的项目公司团队而定,不应用这个,通过css-module编写React的款式也是能够的

当然若是应用了styled-components,便解决了一些问题,例如,款式笼罩,命名等痛点,以及解决了在类申明组件当中,无奈给自定义组件绑定事件的问题

本文只是学习了styled-components的一些罕用的常识,至于更多styled-components具体的应用:能够多查阅styled-components的官网文档的

styled-components 官网文档