乐趣区

关于javascript:React-基础10React-中编写样式-cssstyledcomponents

前言

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 官网文档

退出移动版