tldr;優先使用
PureComponent
,並且永遠不要改變資料物件(或者使用其它最佳實踐)
何時使用Component或PureComponent?
我使用PureComponent
已經有一段時間,這基於它是一個更具效能的元件版本。
事實證明這是真的,但是效能優勢帶來了一些必要前提。
我們來深入研究PureComponent
並且理解為什麼我們應該去使用它。
Component
個PureComponent
只有一個區別
PureComponent
和Component
幾乎完全一樣,唯一的區別是PureComponent
為你處理shouldComponentUpdate
事件。
當props
或state
發生變化,PureComponent
會對兩者都做淺比較
;而Component
則不會對兩者的新舊值進行比較。所以,無論何時呼叫shouldComponentUpdate
,元件都會預設觸發re-render
。
譯者注:
shouldComponentUpdate
預設返回true
,一定觸發re-render
淺比較 101
當對props
和state
的新舊值進行比較時,淺比較
只會檢查基本資料型別
的值是否相等(比如:1
等於1
或者true
等於true
),複雜如物件
和陣列
也是如此,不過是去比較引用值
。
不要改變資料
你可能聽過,不要對props
和state
中的物件
和陣列
進行改變。如果你在父元件對物件
進行了改變,你的純
子元件並不會更新。儘管上游的值發生了改變,但是子元件只會對props
進行引用值
的比較並且無法檢測到區別。
譯者注:這裡改變,指
不改變
物件引用的操作
而正確的做法是,藉助ES6
的object
新特性、array
的擴充套件運算子
或者使用不可變工具庫
來返回新物件
。
譯者注:這裡改變,指
改變
物件引用的操作
以上操作會造成效能問題嗎?
對基本資料型別
的值和引用資料型別
的引用值
比較是個極其廉價的操作。如果你有一組子元件列表並且其中一個更新了,相比於重新渲染所有,在每一個子元件上進行props
和state
的比較仍然是一個非常快的過程。
其他一些注意點
不要在render
中的函式繫結值
比如你父元件裡面有一組子元件列表,每一個都傳給父元件方法一個唯一的引數。為了繫結這個引數,你可能這樣做:
<CommentItem likeComment={() => this.likeComment(user.id)} />
複製程式碼
問題是每一次父元件的render
方法呼叫時,一個新函式(伴隨著新的引用)就會被建立並且傳遞給likeComment
屬性。這會導致一些副作用:每一個子元件的props
都發生改變,最終導致他們全部重新渲染,哪怕資料本身並沒有發生變化。
解決這個問題的方法是,僅僅傳入父元件原型鏈方法的引用給子元件。子元件的likeComment
屬性永遠都有相同的引用值
並且永遠不會引起不必要的re-render
。
<CommentItem likeComment={this.likeComment} userID={user.id} />
複製程式碼
那麼子元件僅需要建立一個類方法並且引用它的props
即可:
class CommentItem extends PureComponent {
...
handleLike() {
this.props.likeComment(this.props.userID)
}
...
}
複製程式碼
不要在render
方法中獲取資料
考慮你的profile
元件需要顯示使用者的10個最受歡迎的文章:
render() {
const { posts } = this.props
const topTen = posts.sort((a, b) => b.likes - a.likes).slice(0, 9)
return //...
}
複製程式碼
元件每次re-render
時,topTen
變數都會是一個全新的引用值
,哪怕posts
變數值沒有發生改變或者slice
的結果也沒有發生變化。但這仍然會對文章列表產生沒有必要的re-render
。
你可以快取你獲取的資料來解決這個問題。比如,把獲取資料操作放入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來建立選擇器
去組合並且快取你獲取的資料。
最後
只要你明確以下兩點,相比於Component
,使用PureComponent
就很安全:
-
改變資料通常是不好的,尤其是使用
PureComponent
時會讓問題更復雜 -
如果你在
render
方法中建立了新函式
,物件
或陣列
,那麼你的做法是錯的
感謝Daniel Min將這篇文章翻譯至Korean