原文: When to use Component or PureComponent?
我開始轉向使用PureCompoent
是由於它是一個更具性能的Component
的版本。雖然事實證實這是正確的,可是這種性能的提升還伴隨着一些附加的條件。讓咱們深挖一下PureComponent
,並理解爲何咱們應該使用它。javascript
除了爲你提供了一個具備淺比較的shouldComponentUpdate
方法,PureComponent
和Component
基本上徹底相同。當props
或者state
改變時,PureComponent
將對props
和state
進行淺比較。另外一方面,Component不會比較當前和下個狀態的props
和state
。所以,每當shouldComponentUpdate
被調用時,組件默認的會從新渲染。java
當把以前和下一個的props
和state
做比較,淺比較將檢查原始值是否有相同的值(例如:1 == 1
或者ture==true
),數組和對象引用是否相同。git
您可能已經據說過,不要在props
和state
中改變對象和數組,若是你在你的父組件中改變對象,你的「pure」子組件不將更新。雖然值已經被改變,可是子組件比較的是以前props
的引用是否相同,因此不會檢測到不一樣。es6
所以,你能夠經過使用es6的assign方法或者數組的擴展運算符或者使用第三方庫,強制返回一個新的對象。github
比較原始值值和對象引用是低耗時操做。若是你有一列子對象而且其中一個子對象更新,對它們的props
和state
進行檢查要比從新渲染每個子節點要快的多。redux
假設你有一個項目列表,每一個項目都傳遞一個惟一的參數到父方法。爲了綁定參數,你可能會這麼作:數組
<CommentItem likeComment={() => this.likeComment(user.id)} />
這個問題會致使每次父組件render方法被調用時,一個新的函數被建立,已將其傳入likeComment
。這會有一個改變每一個子組件props
的反作用,它將會形成他們所有從新渲染,即便數據自己沒有發生變化。緩存
爲了解決這個問題,只須要將父組件的原型方法的引用傳遞給子組件。子組件的likeComment
屬性將老是有相同的引用,這樣就不會形成沒必要要的從新渲染。安全
<CommentItem likeComment={this.likeComment} userID={user.id} />
而後再子組件中建立一個引用了傳入屬性的類方法:函數
class CommentItem extends PureComponent { ... handleLike() { this.props.likeComment(this.props.userID) } ... }
考慮一下你的配置組件將從一系列文章中展現用戶最喜歡的十篇文章。
render() { const { posts } = this.props const topTen = posts.sort((a, b) => b.likes - a.likes).slice(0, 9) return //... }
每次組件從新渲染時topTen
都將有一個新的引用,即便posts
沒有改變而且派生數據也是相同的。這將形成列表沒必要要的從新渲染。
你能夠經過緩存你的派生數據來解決這個問題。例如,設置派生數據在你的組件state
中,僅當posts更新時它才更新。
componentWillMount() { this.setTopTenPosts(this.props.posts) } componentWillReceiveProps(nextProps) { if (this.props.posts !== nextProps.posts) { this.setTopTenPosts(nextProps) } } setTopTenPosts(posts) { this.setState({ topTen: posts.sort((a, b) => b.likes - a.likes).slice(0, 9) }) }
若是你正在使用Redux,能夠考慮使用reselect來建立"selectors"來組合和緩存派生數據。
只要你遵循下列兩個簡單的規則就能夠安全的使用PureComponent
來代替Component
:
- 雖然一般狀況下易變性就是很差的,可是當使用`PureComponent`時問題會變得複雜。 - 若是你在`render`方法中建立一個新的函數,對象或者是數組那麼你的作法(可能)是錯誤的。