共计 12594 个字符,预计需要花费 32 分钟才能阅读完成。
前言
React
是一个构建用户界面的 js 库, 从 UI=render()
这个等式中就很好的映射了这一点,UI
的显示取决于等式左边的 render
函数的返回值.
而编写 React
利用, 就是在编写 React
组件, 组件中最重要的数据就是 props
和state
, 有了数据, 怎么让其以什么样的显示, 那就是 CSS 做的事件了
在 React
中, 所有皆能够是 Js
, 也就是说在js
外面能够写 css
, 这相比传统的内容(html
), 层叠款式(css
), 行为动作(js
) 进行拆散, 这种拆散仅仅是把三个不同的技术进行了物理上的拆散, 进行离开治理, 如果从另一个视觉角度上讲, 并没有实现高内聚的特点
既然前端自身就是页面的展现, 那么把 js
和css
放在一起, 也是一种细粒度的组合,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
, 它是一种模式, 这个css
由js
生成而不是在内部文件中定义, 是CSS Modules
, 次要是借助第三方库生成随机类名称的形式来建设一种部分类名的形式
这种 css-in-js
的第三方模块有很多: 能够拜访:https://github.com/MicheleBertoli/css-in-js
明天的次要学习的是 github
上star
数最多的,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 官网文档