PureComponent介紹react
以前的一篇文章React性能優化方案之PureRenderMixin,是對react的shouldComponentUpdate的方法進行重寫去優化。但自從React15.3中新加了一個 PureComponent 類,易於在本身的組件使用,只須要將Component 換成 PureComponent 便可,因此原做者建議使用今天要介紹的PureComponent。數組
PureComponent原理性能優化
咱們知道組件的state和props的改變就會觸發render,可是組件的state和props沒發生改變,render就不執行,只有PureComponent檢測到state或者props發生變化時,PureComponent纔會調用render方法,所以,你不用手動寫額外的檢查,就能夠達到提升性能的而目的。實際上react作了最外層的淺比較:bash
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
複製代碼
shadowEqual只會淺檢查組件的props和state,因此嵌套對象和數組是不會被比較的。深比較是要逐層遍歷枚舉對應的鍵值進行比對,這個操做比較浪費時間。若是比較的深的狀況,你也可使用shouldComponentUpdate來手動單個比較是否須要從新渲染。最簡單的方式就是直接比較props或state:函數
shouldComponentUpdate(nextProps, nextState) {
return nextProps.xxx.xx === props.xxx.xx;
}
複製代碼
另外,你可使用immutable屬性,Immutable.js就是一個Immutable庫。這種狀況下,屬性的比較是很是容易的,由於已存在的對象就不會發生改變,取而代之的是從新建立新的對象。post
使用PureComponent性能
爬坑1:易變數據不能使用一個引用優化
PureComponent節約了咱們的時間,避免了多餘的代碼。那麼,掌握如何正確使用它是很是重要的,不然若是使用不當,它就沒法發揮做用。例如,讓咱們想一想這樣一種狀況,父組件有一個render方法和一個click處理方法:ui
handleClick() {
let {items} = this.state
items.push('new-item')
this.setState({ items })
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ItemList items={this.state.items} />
</div>
)
}
複製代碼
因爲ItemList是一個純組件,這時候點擊它是不會被渲染的,可是咱們的確向this.state.items加入了新的值,可是它仍然指向同一個對象的引用。可是,經過移除可變對象就很容易改變這種狀況,使之可以正確被渲染。this
handleClick() {
this.setState(prevState => ({
words: prevState.items.concat(['new-item'])
}))
}
複製代碼
爬坑2:不變數據使用一個引用
上面易變數據不能使用一個引用的案例中有一個點擊刪除操做,若是咱們刪除的代碼這麼寫:
constructor(props){
super(props)
this.state={
items: [{a: 1}, {a: 2}, {a: 3}]
}
}
handleClick = () => {
const { items } = this.state;
items.splice(items.length - 1, 1);
this.setState({ items });
}
複製代碼
items 的引用也是改變的,但若是 items 裏面是引用類型數據這時候state.items[0] === nextState.items[0]是false,子組件裏仍是從新渲染了。這樣就須要咱們保證不變的子組件數據的引用不能改變。這個時候可使用前面說的immutable-js函數庫。
爬坑3:父給子經過props傳遞迴調函數
咱們在子父通訊傳遞迴調函數時候:
// step1
<MyInput onChange={e => this.props.update(e.target.value)} />
// step2
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update.bind(this)} />
}
複製代碼
因爲每次 render 操做 MyInput 組件的 onChange 屬性都會返回一個新的函數,因爲引用不同,因此父組件的 render 也會致使 MyInput 組件的 render ,即便沒有任何改動,因此須要儘可能避免這樣的寫法,最好封裝:
update = (e) => {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}
複製代碼
爬坑4:新數組,空數組,也會致使組件從新渲染
<Entity values={this.props.values || []}/>
複製代碼
爲了不這個問題,你可使用defaultProps,它包含了一個屬性的初始化空狀態。在純組件(PureComponent)被建立時,由於函數的新對象被建立了,因此它會得到新數據,而且從新渲染。解決這個問題最簡單的方法就是: 在組件的constructor方法中使用bind。
constructor(props) {
super(props)
this.update = this.update.bind(this)
}
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}
複製代碼
同時,在JSX中,任何包含子元素(child elements)的組件,shallowEqual檢查總會返回false。
PureComponent使用方式
用法很簡單:
import React { PureComponent, Component } from 'react';
class Foo extends (PureComponent || Component) {
//...
}
複製代碼
老版本兼容寫法,在老版本的 React 裏也不會報錯的。
總結
1.純組件忽略從新渲染時,不只會影響它自己,並且會影響它的說有子元素,因此,使用PureComponent的最佳狀況就是展現組件,它既沒有子組件,也沒有依賴應用的全局狀態
2.PureComponent 真正起做用的,只是在一些純展現組件上,複雜組件用了也不要緊,反正 shallowEqual 那一關就過不了,不過記得 props 和 state 不能使用同一個引用。