在react使用中相信大家一定碰到或者使用過Component和PureComponent,但是這兩者具體有什么區(qū)別,實(shí)現(xiàn)這種區(qū)別的原理是什么?(拼多多一面的時(shí)候被問了這個(gè)問題......)
區(qū)別
PureComponent通過prop和state的淺比較來實(shí)現(xiàn)shouldComponentUpdate,某些情況下可以用PureComponent提升性能
所以其實(shí)就是PureComponent在內(nèi)部寫好了shouldComponentUpdate這個(gè)函數(shù),具體的寫法如下:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
// 所做的事情就是對props和state與之前的值進(jìn)行一次淺比較,如果淺比較發(fā)現(xiàn)前后都沒有變化,那么就不觸發(fā)update,否則觸發(fā)update
相比于PureComponent組件,Component組件的shouldComponentUpdate是默認(rèn)返回true的,也就是說每一次setState,無論這次setState是否真的改變了狀態(tài)都會(huì)觸發(fā)update,或者只要父組件重新render了,傳下來的props即使不發(fā)生變化(甚至沒有傳props進(jìn)子組件)那子組件也會(huì)重新update一次,這在很多情況下顯然是多余的更新,所以說PureComponent在一些情況下可以提升性能,但是使用時(shí)也有一些注意點(diǎn),不然容易產(chǎn)生bug
來看一下Component組件的任性render:
class Child extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log('child render')
return <div>hahahah</div>
}
}
class IndexPage extends React.Component{
constructor() {
super();
this.state = {
arr: [1]
};
console.log('constructor');
}
changeState = () => {
const { arr } = this.state;
arr.push('@')
// 這里實(shí)際上arr的指向并沒有發(fā)生變化,但是setState之后還是會(huì)觸發(fā)update,因?yàn)镮ndexPage是繼承自Component組件的,只要setState就會(huì)update,(如果現(xiàn)在改成繼承自PureComponent,那么在shouldComponentUpdate里面的判斷就會(huì)返回false,那么就不會(huì)觸發(fā)update,那么子組件也不會(huì)update了)
this.setState({
arr
})
};
render() {
console.log('render');
return (
<div>
<button onClick={this.changeState}>點(diǎn)擊</button>
<div>{this.state.arr.toString()}</div>
// 我們可以發(fā)現(xiàn),點(diǎn)擊按鈕之后展示的內(nèi)容一致在發(fā)生變化
<Child />
// 父組件沒有傳入props進(jìn)入Child組件,但是只要父組件重新render了,因?yàn)樽咏M件是繼承自Component的,所以默認(rèn)shouldComponentUpdate返回true,所以也會(huì)無腦render,即使和原來一模一樣,(如果把子組件改成繼承自PureComponent,那么即使父組件update,子組件也不會(huì)update,因?yàn)椴]有傳入props到子組件,在子組件的shouldComponentUpdate里面的判斷會(huì)返回false)
</div>
);
}
}
注意點(diǎn)
所以在使用PureComponent時(shí)需要有些注意點(diǎn):
-
在state里面使用復(fù)雜類型的時(shí)候,如數(shù)組,避免直接對數(shù)組進(jìn)行操作而忘記改變指向,這時(shí)即使數(shù)組實(shí)際內(nèi)容發(fā)生了變化也不會(huì)update,像上面例子里面那樣對數(shù)組的操作
const { arr } = this.state; arr.push('@'); this.setState({ arr }) // 這種做法只會(huì)觸發(fā)Component的update而不會(huì)觸發(fā)PureComponent的update,實(shí)際上,不管在哪找寫法里面都不推薦直接對state里面的內(nèi)容進(jìn)行操作,正確的做法應(yīng)該是下面這樣 const { arr } = this.state; const newArr = [...arr]; newArr.push('@'); this.setState({ arr: newArr }) 對于基本數(shù)據(jù)類型來說,值發(fā)生變化PureComponent就會(huì)update,對于復(fù)雜數(shù)據(jù)類型要指向發(fā)生變化
class Example extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isShow: false,
}
}
handleClick = () => {
this.setState({
isShow: true
})
}
render() {
console.log('render');
// 第一次組件初始化render的時(shí)候會(huì)打印一次render
// 第一次點(diǎn)擊按鈕觸發(fā)事件會(huì)觸發(fā)一次render
// 后續(xù)再點(diǎn)擊按鈕就不會(huì)觸發(fā)render了,因?yàn)楹罄m(xù)的isShow的值始終是true,setState也沒有發(fā)生變化
// 如果把組件改成繼承自Component的話,那么所有的點(diǎn)擊都會(huì)觸發(fā)render
return ....
}
}