React渲染机制
触发Render
Reac更新视图必须触发Render,但是这样往往又很影响性能(因为有dom改动),React利用特有的diff算法采用虚拟DOM,是的性能有了很大的提升。
但是我们在开发中还是要注意性能优化,避免没有意义的Render。
触发Render的条件:
- 首次加载组件
- 使用了setState(更新了Props)
- Props更新了(父级传给子级的值改变了)
很多情况下我们可以直接避免很多2、3情况导致的性能问题。
生命周期执行顺序
只执行一次: constructor、componentWillMount、componentDidMount
执行多次:render 、子组件的componentWillReceiveProps、componentWillUpdate、componentDidUpdate
有条件的执行:componentWillUnmount(页面离开,组件销毁时)
不执行的:根组件(ReactDOM.render在DOM上的组件)的componentWillReceiveProps(因为没有父组件给传递props)
//
componentWillMount 在渲染前调用,在客户端也在服务端。
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。
componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。
componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
componentWillUnmount在组件从 DOM 中移除的时候立刻被调用。
React Diff
虽然React高效的diff算法和虚拟dom完美结合,让用户可以‘无限制’刷新而且不用考虑性能问题,但是diff算法还是需要很多时间,如果是很大的模块,不注意Render触发的细节,自然还是很影响性能。
没有优化的例子
功能如下:
- 更新state的值,但是需要调用setState方法
- 传给后代组件的值不改变,即为后代props没有变化
// 父
import React from 'react'
import MockChild from './child/index.js'
export default class Demo5 extends React.Component{
constructor(args){
super(args)
this.state = {
'mockNum': 0
}
}
handleClick(){
this.setState({
'mockNum': 0,
})
}
render(){
console.log('父级state ==============>', this.state)
return(
<div>
<button onClick={this.handleClick.bind(this)}>点击</button>
<MockChild mockNum={this.state.mockNum}/>
</div>
)
}
}
//子
render(){
console.log('子级Props =============>', this.props)
return(
<div></div>
)
}
我们反复触发事件,虽然state的值并没有任何变化,但是我们看打印的结果:

render 重复执行
可能会疑问,我在项目并不会做这么一种无用功的!但实际上,当一个组件逻辑复杂起来之后,会产生各种各样的情况.比如:
比如一个组件的中有个包含onChange事件的input,当我改变该输入框内容的时候调用了setState,渲染视图的时候也会触发子组件的重新render.但我们明明没有做任何和子组件有联系的操作
例子:
//父
state = {
'mockValue': '123'
}
handleChange(e){
this.setState({
'value': '123',
})
}
//render
<input onChange={this.handleChange.bind(this)} />
<MockChild />
/*
* 子组件不做变化
*/

生命周期基础优化
shouldComponentUpdate
执行过程:

它是在render渲染之前触发的,只要在这里加以判断就可以有效阻止 '无用'的render 触发。componentWillReceiveProps也可以,但是它只有props的更新判断,而有时候也不能放过未更改的state!
shouldComponentUpdate(nextProps, nextState){
if(nextState !== this.state && nextProps !== this.props) return true;
return false
}
这样可以有效的解决冗余的Render,当项目很大的时候,就需要更准确的判断到某一个值
shouldComponentUpdate(nextProps, nextState){
if(nextState.xxx !== this.state.xxx && nextProps.xxx !== this.props.xxx) return true;
return false
}
componentWillReceiveProps
几种情况:
1、componentWillReceiveProps函数有一个参数nextProps,它是一个 { 对象 } ,它是update时候(下一次)父组件传递过来的props。
2、有些生命周期函数只执行一次,而有的执行多次,其中componentWillReceiveProps执行多次,而constructor等执行一次。
3、还需知道在子组件中每次传递过来的this.props对象其实和componentWillReceiveProps的nextProps是一样的,都是最新的。
4、componentWillReceiveProps生命周期是在更新子组件最先执行的,优先于compoentWillUpdate,更优先于render。
5、render函数里不能使用setState(),否则会造成死循环。
大部分情况下 componentWillReceiveProps 生命周期函数是没用的,即可以略去不写,
但是在constructor函数中初始化了某个state,必须用 componentWillReceiveProps 来更新state,不可省去,否则render中的state将得不到更新。
同时如果您想在子组件监听watch值变化做处理,也可以用到componentWillReceiveProps
使用componentWillReceiveProps的时候,不要去向上分发,调用父组件的相关setState方法,否则会成为死循环。
参考文章
1、www.cnblogs.com/soyxiaobi/p… 【React的渲染机制】
2、www.cnblogs.com/soyxiaobi/p… 【React生命周期执行】