✉️前言
对于 react
的表层来说,我们可能经常听说的是声明式开发、单向数据流、组件化方式开发等等。那么,进一步来讲的话,我们还需要了解的是 react
的 props
,虚拟 DOM
、 ref
以及过渡动画等更多新的知识点。
那在下面的这篇文章中,将来探索关于 react
的进阶知识。
叮,下面开始本文的介绍~🤪
📧一、props
1、PropTypes与DefaultProps应用
(1)PropTypes
在 react
中,有时候我们要对组件中的某个属性进行格式校验,这个时候我们就需要用到 propTypes
。下面给出一些常见的例子:
// 表示要对TodoItem这个组件做属性的校验
TodoItem.propTypes = {
// isRequired 表示必须有值
test: PropTypes.string.isRequired,
// 表示content的propTypes必须是string类型,那string类型的校验是从PropTypes这个包里面拿到的
content: PropTypes.string,
// 表示detail可以是一个number或者string的语法
detail: PropTypes.arrayOf(PropTypes.number, PropTypes.string),
// 表示deleteItem必须是一个函数
deleteItem: PropTypes.func,
// 表示index必须是一个数字类型
index: PropTypes.number
}
(2)defaultProps
有时候,对于某个属性来说,我们希望给它个初始值,那这个时候就需要用到 defaultProps
。示例如下:
TodoItem.defaultProps = {
test: 'hello world'
}
上面的代码表明,当 test
属性没有被赋值时,那么它将被赋予一个初始值,值为 hello world
。
2、props,state与render函数
在 react
中,定义一个组件时,经常会看到 props
、 state
和 render
。那他们三者是怎么样的关系呢?
首先我们要想一个问题:为什么数据发生变化,页面就会跟着变化呢?
原因在于,页面是由 render
函数渲染出来的,当数据 state
发生变化时, render
函数就会被重新的执行一次。
同时,当父组件的 render
函数被运行时,它的子组件的 render
都将被重新运行。
📨二、React中的虚拟DOM
1、什么是虚拟DOM
(1)第一种方案
传统实现虚拟 DOM 的思路:
- 先定义
state
,也就是数据; - 编写
JSX
模板内容; - 把数据和模板进行结合,生成真是的
DOM
,进而将内容显示到页面上; - 如果遇到要替换数据时,则把数据和模板进行结合,生成真实的
DOM
,来替换原始的DOM
。
存在缺陷:
- 第一次生成了一个完整的
DOM
片段; - 第二次又生成了一个完整的
DOM
片段; - 第二次的
DOM
替换第一次的DOM
,非常耗费性能。
(2)第二种方案
传统实现虚拟 DOM 的思路改进版:
- 先定义
state
,即数据; - 编写
JSX
模板内容; - 把数据和模板进行结合,生成真实的
DOM
,并展示; - 当
state
的数据发生改变; - 继续,把数据和模板进行结合,生成真实的
DOM
,并不是直接替换原始的DOM
- 将新的
DOM
和原始的DOM
做比较,并找出差异; - 找到新的
Dom
中发生的变化; - 只用新的
DOM
中变化的数据,来替换掉老的DOM
中的数据。
存在缺陷:
- 性能的提升并不明显
(3)第三种方案
react 中实现虚拟 DOM 的思路:
先定义
state
,即数据;编写
JSX
模板内容;把数据和模板进行结合,生成虚拟
DOM
(虚拟DOM
就是一个JS
对象,用它来描述真实DOM
)。👉(损耗了性能)<div id="abc"><span>hello world</span></div> ['div', { id: 'abc'}, ['span', { }, 'hello world']]
用虚拟
DOM
的结构生成真实的DOM
,来进行显示;当
state
发生变化时,数据 + 模板生成新的虚拟DOM
;👉(极大的提升了性能)
<div id="abc"><span>monday</span></div>
['div', {
id: 'abc'}, ['span', {
}, 'monday']]
- 比较 原始虚拟
DOM
和 新的虚拟DOM
的区别,找到其中的区别是span
中的内容; - 直接操作
DOM
,改变span
的内容; - 因此,
React
的虚拟DOM
主要经历的过程是:JSX
→createElement
→ 虚拟DOM
(JS
对象 )→ 真实的DOM
。
react 虚拟 DOM 的优点:
- 极大的提升了性能;
- 它使得跨端应用得以实现,这要谈到
react
中的一个概念,react native
; react
使用可以编写原生应用,像Android
和IOS
开发,这些都是操作真实DOM
;- 而
react
使得编写这些原生应用得以使用。
2、虚拟DOM中的 Diff 算法
react
对setState
的性能优化,它会把多次setState
结合成一次setState
;- 虚拟
DOM
使用diff
算法做比较,只在同层做比较,不跨级做比较; - 同层比对的算法比较简单,而算法简单带来的直接好处就是速度非常快;
- 虽然可能会造成
DOM
渲染上的一些浪费,但是呢,它也极大的减少了两个虚拟DOM
之间进行比较时,性能上的消耗。
📩三、React中ref的使用
react
中建议的是,希望我们可以用数据驱动的形式来编写代码,尽量不要操作 DOM
。但有时候,我们在做一些极其复杂业务的时候,比如各种震撼动画,不可避免的还是会用到一些原生的 DOM
标签。因此, ref
帮助我们在 react
中直接获取 DOM
元素的时候来进行使用。
一般情况下,我们尽量不使用 ref
。如果用 ref
时,会出现各种各样的问题。同时,当使用 ref
和 setState
时,要注意一些存在的坑。
比如,当 ref
与 setState
相关联使用时,要注意, setState
是一个异步函数,往往会在同步代码执行完毕后再执行异步代码。因此,如果我们希望同步代码执行顺序在 setState
之后时,可以在 setState 接受的第二个参数中,再增加一个回调函数来进行调用,这样就可以达到我们的效果啦!
render() {
return (
<ul ref={
(ul) => this.ul = ul}>
{
this.getTodoItem()}
</ul>
)
}
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}), () => {
console.log(this.ul.querySelectorAll('div').length)
});
📦四、React中的生命周期
1、生命周期函数是什么
所谓生命周期函数,指的是在某一个时刻,组件会自动调用执行的函数。那 react
的生命周期都有哪一些呢?
阶段 | 生命周期 | 含义 |
---|---|---|
Mounting | componentWillMount | ①当组件即将被挂载到页面的时刻时自动执行,即在页面挂在之前执行;②只在组件被第一次挂在到页面上才会执行; |
Mounting/Updation | render | 页面挂载时被执行 |
Mounting | componentDidMount | 会在组件被挂载到页面之后,自动被执行;只在组件被第一次挂在到页面上才会执行 |
Updation | componentWillReceiveProps | ①当一个组件从父组件接收参数;②只要父组件的render函数被执行了,子组件的这个生命周期函数就会被执行;③如果这个组件第一次存在于父组件中,不会执行;④如果这个组件之前已经存在于父组件中,才会被执行; |
Updation | componentWillUpdate | ①组件被更新之前,她会自动执行;②但是它是在shouldComponentUpdate之后被执行,如果shouldComponentUpdate返回true时,它才执行;如果返回false,这个函数就不会被执行了。 |
Updation | componentDidUpdate | 组件更新完成之后,它会被执行。 |
Updation | shouldComponentUpdate | 组件被更新之前,它会被自动被执行;此生命周期返回一个布尔值 |
Unmounting | componentWillUnmount | 当这个组件即将被从页面中剔除的时候,会被执行。 |
2、生命周期图例
下面用一张图来展示 react
中生命周期的执行效果:
📪五、React中使用CSS动画效果
1、普通用法
我们来看一下,在 react
中,如何使用 css3
所提供的动画效果。具体代码如下:
.show {
animation: show-item 2s ease-in forwards;
}
.hide {
animation: hide-item 2s wase-in forwards;
}
@keyframes show-item {
0% {
oppacity: 0;
color: red;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 1;
color: blue;
}
}
2、react-transition-group
(1)初次探索
有时候,我们可能会想要实现一些很复杂的动画,这个时候 css3
提供的是不够的。因此,我们还需要一点 js
来加以辅助实现更为复杂的动画。这个时候就有谈到 react
中的 react-transition-group
动画。
假设我们想要实现,当点击一个按钮时,一行文字渐隐渐显的实现,那该怎么处理呢?
首先,我们在项目的 src
文件夹下新增一个组件,命名为 App.js
。具体代码如下:
import React, {
Component, Fragment } from 'react';
import {
CSSTransition } from 'react-transition-group';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
show: true
}
this.handleToggle = this.handleToggle.bind(this);
}
render() {
return (
<Fragment>
{
/* onEntered指的是在某一个时刻会自动执行的一个函数
当入场动画结束时,onEntered将会被执行
*/}
<CSSTransition
in={
this.state.show}
timeout={
1000}
classNames='fade'
unmountOnExit
onEntered={
(el) => {
el.style.color = "blue" }}
appear={
true}
>
<div>hello</div>
</CSSTransition>
<button onClick={
this.handleToggle}>toggle</button>
</Fragment>
)
}
handleToggle() {
this.setState({
show: this.state.show ? false : true
})
}
}
export default App;
之后新增一个 CSS
文件,命名为 style.css
。具体代码如下:
/* 入场动画执行的第一个时刻,即刚要入场的这个瞬间 */
/* fade-appear用于实现渐隐渐现的效果 */
.fade-enter,
.fade-appear {
opacity: 0;
}
/* 入场动画执行的第二个时刻,到入场动画执行完成之前的一个时刻 */
.fade-enter-active,
.fade-appear-active {
opacity: 1;
transition: opacity 1s ease-in;
}
/* 当整个入场动画执行完成之后 */
.fade-enter-done {
opacity: 1;
}
/* 表示出场动画执行的第一个时刻 */
.fade-exit {
opacity: 1;
}
/* 整个出场的过程 */
.fade-exit-active {
opacity: 0;
transition: opacity 1s ease-in;
}
/* 当整个出场动画执行完成时 */
.fade-exit-done {
}
此时我们来看下浏览器的显示效果:
(2)进阶探索
上面我们只是改变了一项数据。现在,如果我们想要点击就新增一项过渡效果,这又该如何处理呢?
我们来改造下 App.js
文件的代码。具体代码如下:
import React, {
Component, Fragment } from 'react';
import {
CSSTransition, TransitionGroup } from 'react-transition-group';
import './style.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
show: true,
list: []
}
this.handleToggle = this.handleToggle.bind(this);
}
render() {
return (
<Fragment>
{
/* onEntered指的是在某一个时刻会自动执行的一个函数
当入场动画结束时,onEntered将会被执行
*/}
<TransitionGroup>
{
this.state.list.map((item, index) => {
return (
<CSSTransition
in={
this.state.show}
timeout={
1000}
classNames='fade'
unmountOnExit
onEntered={
(el) => {
el.style.color = "blue" }}
appear={
true}
key={
index}
>
<div>{
item}</div>
</CSSTransition>
)
})
}
</TransitionGroup>
<button onClick={
this.handleToggle}>toggle</button>
</Fragment>
)
}
handleToggle() {
this.setState((prevState) => {
return {
list: [...prevState.list, 'item']
}
})
}
}
export default App;
此时浏览器的运行效果如下:
对于这种类型的动画来说,我们通过 TransitionGroup
对外层进行包裹,之后通过 CSSTransition
对里层进行包裹,进而达到我们最终的效果。
📮六、结束语
在上面这篇文章中,我们讲解了 react
中的 props
,同时,还简单的了解了虚拟 DOM
的内容。除此之外呢,还学习了 ref
的使用,以及 react
中的酷炫的过渡动画。当然,最为重要的一点是, react
中的生命周期函数。
那到这里, react
的进阶知识讲到这里就结束了。不知道小伙伴们对 react
是否又有进一步的认识呢?
🗳️彩蛋
- 关注公众号星期一研究室,第一时间关注优质文章,更有面试专栏待你解锁~
- 如果您觉得这篇文章有帮助到您的的话不妨点赞支持一下哟~~😉
- 以上就是本文的全部内容!我们下期见!🥂🥂🥂