前言
上篇文章已经带你学习了react的引进方式与项目的启动,以及react的元素、组件、父组件向子组件传参props(只能读不能写),以及state(内部数据)的使用;今天主要讲解react的生命周期函数;
生命周期函数(lifecycle)
我们使用js创建并渲染元素的过程如下:
let div=document.createElement('div')
// 这是div的create/construct过程
div.textContent = 'hi'
// 这是初始化state
document.body.appendChild(div);
// 这是div的mount过程
div.textContent = 'h2'
// 这是div的update过程
div.remove()
// 这是div的unmount过程
同样的react组件也是有这些过程,我们称之为生命周期,而生命周期函数就是每个生命阶段所调用的函数;
constructor函数
用途:
- 初始化props
- 初始化state,但此时不能使用setState()
如果不使用state,则可以不写constructor此函数,如果要使用state,则必须要使用constructor此函数
constructor(){
super();
this.state = {
n:0
}
}
shouldComponentUpdate函数
用途:
- 返回true,则不阻止UI更新
- 返回false,则阻止UI更新
- 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新;
我们使用之前的代码做示范
import React from 'react';
class SonChild extends React.Component {
constructor(){
super();
// 初始化
this.state = {
n:0
}
}
add = ()=>{
this.setState(state=>({n:state.n+1}))
this.setState(state=>({n:state.n-1}))
}
render(){
console.log('render了')
return(
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
)
}
}
export default SonChild;
上面的代码,我们点击按钮,n最后的值还是0,所以页面都是0,但是控制台每点击按钮都会打印一次'render了'' 220.gif

import React from 'react';
class SonChild extends React.Component {
constructor(){
super();
// 初始化
this.state = {
n:0
}
}
add = ()=>{
this.setState(state=>({n:state.n+1}))
this.setState(state=>({n:state.n-1}))
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
return !(nextState.n === this.state.n)
}
render(){
console.log('render了')
return(
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
)
}
}
export default SonChild;
以上代码就阻止了render



render函数
- 作用:展示视图,用来告诉浏览器组件的样子
render(){
return (<div></div>)
}
// <div></div>这部分代码不是dom对象,是表示dom的对象,是虚拟dom
我们可以打印出虚拟dom
render(){
const x = (
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
)
console.log('render了', x)
return x
}

- 只能有一个根元素
render(){
const x = (
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
<div>我是第二个根元素</div>
)
return x
}
上面代码有两个根元素,运行后会报错;

render(){
const x = (
<React.Fragment>
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
<div>我是第二个根元素</div>
</React.Fragment>
)
return x
}
上面代码运行正常,使用<React.Fragment>与使用
的区别是,渲染页面的dom结构不一样;

上面的图片是使用<React.Fragment>标签

上面的图片是使用div标签



从上面两个渲染结果来看,使用div来作为最外层标签比React.Fragment,渲染的时候会多个div标签;
React会认为React.Fragment写法也是太麻烦了,于是做了简络,使用<></>与使用<React.Fragment></React.Fragment>效果一样;
render(){
const x = (
<>
<div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={this.add}>+1</button></div>
<div>我是第二个根元素</div>
</>
)
return x
}
componentDidMount 页面加载完成后触发
作用:主要用来操作页面上的dom
- ajax请求可以写在此钩子
- 首次渲染会执行此钩子函数
componentDidUpdate 视图更新后执行代码
- 此处也可以加载ajax请求,用来更新数据
- 首次渲染不会执行此钩子函数
componentWillUnmount 组件将要移除页面并且被销毁时触发(组件从内存销毁时触发);unMount过的组件不会再mount
使用场景: - 如果在componentDidMount里面设置了scroll监听事件,就需要在componentWillUnmount里面取消此监听事件
- 如果在componentDidMount里面创建了Timer,就需要在componentWillUnmount里面删除Timer
- 如果在componentDidMount里面创建了ajax请求,就需要在componentWillUnmount里面删除请求
原则谁污染谁治理,否则一些不用的监听事件、timer,ajax请求就会占用户的内存;
