1、constructor() :初始化 props、state、绑定 this
React 类组件的 constructor() 生命周期,主要用途是 初始化 props、初始化 state、绑定 this。若该类组件只需要初始化 props ,则 constructor 可以不写。如果 constructor 中需要写其他东西,比如:声明 state、绑定 this,则 constructor 不能省略。constructor 接收一个 props 外部数据的参数,具体使用方法如下:
class Welcome extends React.Component { constructor(props){ //如果没有额外的代码,这三行可省略,不写也可以初始化props数据 super(props); //初始化 props } } class Welcome extends React.Component { constructor(props){ super(props); //如果constructor中不只需要初始化 props,则那三行不可省略 this.state = { //初始化 state,只能通过这种 this.state = {} 的方式初始化state n: 1; } } } class Welcome extends React.Component { class Welcome extends React.Component { constructor(props){ constructor(props){ super(props); super(props); this.state = {} this.state = {} //bind this,可以写成右面这种语法 } this.onClick = this.onClick.bind(this); onClick = ()=> {} //和左边是等价的,推荐这种 } } }
2、shouldComponentUpdate() :return false 阻止更新
React 类组件的 shouldComponentUpdate() 生命周期,主要用途是:允许开发人员手动判断是否要进行组件更新。shouldComponentUpdate() 接收两个参数,一个是新的 props,一个是新的 state,该函数如果返回 true 表示不阻止 UI 更新,返回 false 表示阻止 UI 更新。我们可以根据应用场景灵活地设置返回值,以避免不必要的更新。
what?这是什么鬼逻辑,React 为什么会需要开发人员去决定组件是否更新?我们需要来看一个示例:
class App extends React.Component { //React.PureComponent内置了shouldComponentupdate钩子 constructor(props){ super(props); //初始化props this.state = { //声明 state n: 1 }; } onClick = () => { this.setState(state =>({n:state.n+1})) //进行 +1 操作 this.setState(state =>({n:state.n-1})) //再进行 -1 操作 }; shouldComponentUpdate(newprops, newstate){ //nextprops、nextstate if(newstate.n === this.state.n){ return false; }else{ return true; } } render(){ console.log('render了一次'); return ( <div> {this.state.n} <button onClick={this.onClick}>+1-1</button> </div> ); } }
有时候在类组件中某个操作功能比较复杂,但是它最终的操作结果和一开始一样,比如上面的 +1-1 操作。这个时候虽然数据从最开始 {n:1} 变成了 内存地址不同 的 {n:1},但其值实际上并没有发生改变,所以就不需要重新渲染,这个时候就需要 shouldComponentUpdate 手动阻止 UI 更新,以 提高性能。
正常情况下,如果不加这一层 shouldComponentUpdate 钩子,React 检测到 +1-1 后的结果内存地址不一样,就会认为数据发生了变化,然会后去执行 render() 生成新的虚拟 DOM 对象,然后进行新旧虚拟 DOM 对比,如果新旧 DOM 不同就更新 UI。如果手动加上 shouldComponentUpdate 钩子,则就不需要后面的 render() 步骤,从而提高性能。
疑问:React 这么牛,为什么不能内置这个功能呢?将 newState 和 this.state 的每个属性都对比一下,如果全都相等,就不更新,如果有一个不等,就更新。React 确实内置了,这个功能叫做 React.PureComponent,可以代替 React.Component。PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
3、render() :创建虚拟 DOM
React 类组件的 render() 生命周期,主要用途是展示视图,它能返回一个虚拟 DOM 对象:return(<div>...</div>),但是这个元素 只能有一个根元素,如果需要返回两个根元素,就要用 <React.Fragment> 包起,<React.Fragment/>可以缩写成 <></>。这个包裹的元素只是占位符,实际上并不会被渲染到页面上。
render() 里面可以写 if...else、三木运算符,但是不能直接写 for 循环,需要用数组的方式,可以写 array.map(循环)
render() { render() { let message; return ( if(this.state.n%2 = 0) { <> message=<div>偶数</div>; {this.state.n%2===0 ? }else{ <div>偶数</div> : message=<span>奇数</span>; <span>奇数</span>} } <button onClick = {this.onClick}>+1</button> return ( </> <> ) {message} } <button onClick = {this.onClick}>+1</button> </> ) } render(){ //在循环时必须要写 key,提高 Dom diff 性能 return this.state.array.map( item => <span key={item}> {item} </span>) }
4、componentDidMount() :组件已经出现在页面
React 类组件的 componentDidMount() 生命周期,主要用途是:在元素插入页面后执行代码,这些代码一般是要依赖 DOM(比如:你想获取 div 的高度,就最好在这里写此处)。可以发起 加载数据 的 AJAX 请求(官方推荐)。
⚠ 区别于 componentDidUpdate 生命周期,该生命周期在页面在首次渲染时,一会被执行。
componentDidMount(){ const div = document.getElementById('xxx') //可以使用ref获取元素的 const {width} = div.getBoundingClientRect() this.setState(() => {width: 100}) } class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); //使用React.createRef()创建refs } render() { return <div ref={this.myRef} />; //将 ref 挂到某个元素上 } componentDidMounted(){ const div = this.divRef.current; //通过 current 属性对节点的引用进行访问 const {width} = div.getBoundingClientRect(); this. setState({width}); } }
5、componentDidUpdate() :组件已更新
React 类组件的 componentDidUpdate() 生命周期,会在视图更新后执行相关代码,此处也可以发起用于 更新数据 AJAX 请求(比如用户 id 变了,需要获取新的用户信息)。如果在此钩子内 setState 可能会引起无限循环,除非放在 if 里,所以最好不要在该钩子内改数据。若 shouldComponentUpdate() 生命周期返回 false,表示阻止 UI 更新,则不触发此钩子。
⚠ 区别于 componentDidMount() 生命周期,该钩子在页面首次渲染时是 不会 被执行的。
componentDidupdate(prevProps, prevstate, snapshot)
6、componentWillUnmount() :组件将死
React 类组件的 componentWillUnmount() 生命周期,主要用途是:组件将要被移出页面然后被销毁时执行代码。注意:unmount 过的组件不会再次 mount,因为它也被从内存中销毁了。
- 如果你在 componentDidMount 里面监听了 window scroll 那么你就要在 componentWillUnmount里面取消监听 - 如果你在 componentDidMount 里面创建了 Timer 那么你就要在 componentWillUnmount 里面取消 Timer - 如果你在 componentDidMount里面创建了AJAX请求 那么你就要在 componentWillUnmount里面取消请求
7、分阶段看生命周期钩子的执行顺序
// 首次渲染: // constructor -> render -> componentDidMount // 再次渲染: // 1. props change -\ /--结束 // 2. setState() ---->shouldComponentUpdate-< // 3. forceUpdate()-/ \--render-> 更新UI->componentDidUpdate // 销毁: // componentWillUnmount