【譯】何時使用Component或PureCompoent?

zhCN_超發表於2019-02-26
【譯】何時使用Component或PureCompoent?

原文- When to use Component or PureComponent

tldr;優先使用PureComponent,並且永遠不要改變資料物件(或者使用其它最佳實踐)

何時使用Component或PureComponent?

我使用PureComponent已經有一段時間,這基於它是一個更具效能的元件版本。

事實證明這是真的,但是效能優勢帶來了一些必要前提。

我們來深入研究PureComponent並且理解為什麼我們應該去使用它。

ComponentPureComponent只有一個區別

PureComponentComponent幾乎完全一樣,唯一的區別是PureComponent為你處理shouldComponentUpdate事件。

propsstate發生變化,PureComponent會對兩者都做淺比較;而Component則不會對兩者的新舊值進行比較。所以,無論何時呼叫shouldComponentUpdate,元件都會預設觸發re-render

譯者注:shouldComponentUpdate預設返回true,一定觸發re-render

淺比較 101

當對propsstate的新舊值進行比較時,淺比較只會檢查基本資料型別的值是否相等(比如:1等於1或者true等於true),複雜如物件陣列也是如此,不過是去比較引用值

不要改變資料

你可能聽過,不要對propsstate中的物件陣列進行改變。如果你在父元件對物件進行了改變,你的子元件並不會更新。儘管上游的值發生了改變,但是子元件只會對props進行引用值的比較並且無法檢測到區別。

譯者注:這裡改變,指不改變物件引用的操作

而正確的做法是,藉助ES6object新特性、array擴充套件運算子或者使用不可變工具庫來返回新物件

譯者注:這裡改變,指改變物件引用的操作

以上操作會造成效能問題嗎?

基本資料型別的值和引用資料型別引用值比較是個極其廉價的操作。如果你有一組子元件列表並且其中一個更新了,相比於重新渲染所有,在每一個子元件上進行propsstate的比較仍然是一個非常快的過程。

其他一些注意點

不要在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

相關文章