1、官方文檔解釋
在 React 中,Component 和 PureComponent 有一些區別,官方的解釋如下:
React.Component
is the base class for React components when they are defined using ES6 classes:
class Greeting extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
See the React.Component API Reference for a list of methods and properties related to the base
React.Component
class.
中文的意思是:React.Component 基於 React 的組件,使用 ES6 的類語法(class)可以點擊鏈接查看一系列的方法和屬性。
React.PureComponent
React.PureComponent
is similar toReact.Component
. The difference between them is thatReact.Component
doesn’t implementshouldComponentUpdate()
, butReact.PureComponent
implements it with a shallow prop and state comparison.
If your React component’s
render()
function renders the same result given the same props and state, you can useReact.PureComponent
for a performance boost in some cases.
Note
React.PureComponent
’sshouldComponentUpdate()
only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extendPureComponent
when you expect to have simple props and state, or useforceUpdate()
when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.Furthermore,
React.PureComponent
’sshouldComponentUpdate()
skips prop updates for the whole component subtree. Make sure all the children components are also “pure”.
中文意思:React.PureComponent 與 React.Component 基本相同。
區別:Component 沒有直接實現 shouldComponentUpdate 這個方法;但是 PureComponent通過淺層的Porps 和 state 的對比,內部實現了這個生命週期函數。
如果你的組件 render 函數渲染時具有相同的 props 和 state,那麼可以使用 PureComponent 來提高性能。
注意:PureComponent僅僅實現對象的淺對比。如果對象中包含複雜的數據結構,會產生較大的區別。除非你的state 和 props 是簡單的,或者當你的深層數據結構變化時使用 forceUpdate,或者使用 immutable 對象來快速比較嵌套(複雜)的數據。
此外:PureComponent會跳過整個組件子樹的props更新,要確保全部的子組件也是 pure 的形式。
2、區別具體說明
2.1 性能不同
它們幾乎相同,PureComponent 通過prop、state的淺比較來實現 shouldComponentUpdate,某些情況下可以用 PureComponen t提升性能.
1.淺對比(shallowEqual),即 react 中的一個函數,然後根據下面的方法進行是不是PureComponent
的判斷,幫我們做了本來應該我們在shouldComponentUpdate
中做的事情。
小結:Component 中需要手動執行的 ComponentShouldUpdate 函數,在PureComponent中已經自動完成了(自動淺對比)。
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
而本來我們做的事情如下,這裏判斷了state
有沒有發生變化(prop同理),從而決定要不要重新渲染,這裏的函數在一個繼承了Component
的組件中,而這裏this.state.person
是一個對象,你會發現,在這個對象的引用沒有發生變化的時候是不會重新render
的(即下面提到的第三點),所以我們可以用shouldComponentUpdate
進行優化,這個方法如果返回false
,表示不需要重新進行渲染,返回true
則重新渲染,默認返回true
shouldComponentUpdate(nextProps, nextState) {
return (nextState.person !== this.state.person);
}
2.如下顯示的是一個 Test 組件,設置了一個state
是isShow
,通過一個按鈕點擊可以改變它的值,結果是:初始化的時候輸出的是constructor
,render
,而第一次點擊按鈕,會輸出一次render,即重新渲染了一次,界面也會從顯示false
變成顯示true
,但是當這個組件是繼承自PureComponent
的時候,再點擊的時,不會再輸出render
,即不會再重新渲染了,而當這個組件是繼承自Component
時,還是會輸出render
,還是會重新渲染,這時候就是PureComponent
內部做了優化的體現。同理也適用於string
,number
等基本數據類型,因爲基本數據類型,值改變了就算改變了。
小結:如果傳值都是簡單對象,可以放心使用;如果傳值有複雜對象,需要慎重使用。
import React, { PureComponent } from 'react';
class Test extends PureComponent{
constructor() {
super();
this.state = {
isShow: false
};
}
changeState = () => {
this.setState({ isShow: true });
};
render() {
return (
<div>
<button onClick={this.changeState}>Click it</button>
<div>{this.state.isShow.toString()}</div>
</div>
);
}
}
5.當這個this.state.arr
是一個數組時,且這個組件是繼承自PureComponent
時,初始化依舊是輸出constructor
和render
,但是當點擊按鈕時,界面上沒有變化,也沒有輸出render
,證明沒有渲染,但是我們可以從下面的註釋中看到,每點擊一次按鈕,我們想要修改的arr
的值已經改變,而這個值將去修改this.state.arr
,但是因爲在PureComponent
中淺比較
這個數組的引用沒有變化所以沒有渲染,this.state.arr
也沒有更新,因爲在this.setState()
以後,值是在render
的時候更新的。
小結:如果是複雜數據類型,這裏會造成錯誤的顯示(setState淺複製更新,但是界面不會重新渲染)
6.但是當這個組件是繼承自Component
的時候,初始化依舊是輸出constructor
和render
,但是當點擊按鈕時,界面上出現了變化,即我們打印處理的arr
的值輸出,而且每點擊一次按鈕都會輸出一次render
,證明已經重新渲染,this.state.arr
的值已經更新。
小結:如果是複雜數據類型,使用Component可以正確顯示
import React, { PureComponent } from 'react';
class Test extends PureComponent{
constructor() {
super();
this.state = {
arr:['1']
};
}
changeState = () => {
let { arr } = this.state;
arr.push('2');
console.log(arr);
// ["1", "2"]
// ["1", "2", "2"]
// ["1", "2", "2", "2"]
this.setState({ arr });
};
render() {
return (
<div>
<button onClick={this.changeState}>點擊</button>
<div>
{this.state.arr.map((item) => { return item; })}
</div>
</div>
);
}
}
7.下面的例子用擴展運算符
產生新數組,使this.state.arr
的引用發生了變化,所以初始化的時候輸出constructor
和render
後,每次點擊按鈕都會輸出render
,界面也會變化,不管該組件是繼承自Component
還是PureComponent
的
小結:如果state或者Props是深複製,那麼兩種方法都可以實現更新
import React, { PureComponent } from 'react';
class Test extends PureComponent{
constructor() {
super();
this.state = {
arr:['1']
};
}
changeState = () => {
let { arr } = this.state;
this.setState({
arr: [...arr, '2']
})
};
render() {
console.log('render');
return (
<div>
<button onClick={this.changeState}>點擊</button>
<div>
{this.state.arr.map((item) => {
return item;
})}
</div>
</div>
);
}
}
2.2. 對子組件的影響
PureComponent不僅會影響本身,而且會影響子組件,所以PureComponent最好用在數據展示組件中
1.我們讓IndexPage
組件裏面包含一個子組件Example
來展示PureComponent
是如何影響子組件的
2.父組件繼承PureComponent
,子組件繼承Component
時:下面的結果初始化時輸出爲constructor
,IndexPage render
,example render
,但是當我們點擊按鈕時,界面沒有變化,因爲這個this.state.person
對象的引用沒有改變,只是改變了它裏面的屬性值所以儘管子組件是繼承Component
的也沒有辦法渲染,因爲父組件是PureComponent
,父組件根本沒有渲染,所以子組件也不會渲染
3.父組件繼承PureComponent
,子組件繼承PureComponent
時:因爲渲染在父組件的時候就沒有進行,相當於被攔截了,所以子組件是PureComponent
還是Component
根本不會影響結果,界面依舊沒有變化
4.父組件繼承Component
,子組件繼承PureComponent
時:結果和我們預期的一樣,即初始化是會輸出constructor
,IndexPage render
,example render
,但是點擊的時候只會出現IndexPage render
,因爲父組件是Component
,所以父組件會渲染,但是
當父組件把值傳給子組件的時候,因爲子組件是PureComponent
,所以它會對prop
進行淺比較,發現這個person
對象的引用沒有發生變化,所以不會重新渲染,而界面顯示是由子組件顯示的,所以界面也不會變化
5.父組件繼承Component
,子組件繼承Component
時:初始化是會輸出constructor
,IndexPage render
,example render
,當我們第一次點擊按鈕以後,界面發生變化,後面就不再改變,因爲我們一直把它設置爲sxt2,但是每點擊一次都會輸出IndexPage render
,example render
,因爲每次不管父組件還是子組件都會渲染
6.所以正如下面第四條說的,如果state
和prop
一直變化的話,還是建議使用Component
,並且PureComponent
的最好作爲展示組件
import React, { PureComponent, Component } from 'react';
import Example from "../components/Example";
class Test extends PureComponent{
constructor() {
super();
this.state = {
person: {
name: 'sxt'
}
};
}
changeState = () => {
let { person } = this.state;
person.name = 'sxt2';
this.setState({ person });
};
render() {
const { person } = this.state;
return (
<div>
<button onClick={this.changeState}>點擊</button>
<Example person={person} />
</div>
);
}
}
import React, { Component } from 'react';
class Example extends Component {
render() {
const { person } = this.props;
return(
<div>{person.name}</div>
);
}
}
小結:如果 state props 是複雜(引用)類型,則要引用內存地址不同,纔會正確渲染組件
如果prop和state每次都會變,那麼PureComponent的效率還不如Component,因爲你知道的,進行淺比較也是需要時間
若有shouldComponentUpdate,則執行它,若沒有這個方法會判斷是不是PureComponent,若是,進行淺比較繼承自Component
的組件,若是shouldComponentUpdate
返回false
,就不會渲染了,繼承自PureComponent
的組件不用我們手動去判斷prop
和state
,所以在PureComponent
中使用shouldComponentUpdate
會有如下警告:
Test has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.
也是比較好理解的,就是不要在PureComponent
中使用shouldComponentUpdate
,因爲根本沒有必要.
3、參考資料
官方文檔 https://reactjs.org/docs/react-api.html
https://www.jianshu.com/p/c41bbbc20e65