React 的 PureComponent 與 Component 區別

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 to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.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 use React.PureComponent for a performance boost in some cases.

Note

React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extend PureComponent when you expect to have simple props and state, or use forceUpdate() when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.

Furthermore, React.PureComponent’s shouldComponentUpdate() 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 組件,設置了一個stateisShow,通過一個按鈕點擊可以改變它的值,結果是:初始化的時候輸出的是constructorrender,而第一次點擊按鈕,會輸出一次render,即重新渲染了一次,界面也會從顯示false變成顯示true,但是當這個組件是繼承自PureComponent的時候,再點擊的時,不會再輸出render,即不會再重新渲染了,而當這個組件是繼承自Component時,還是會輸出render,還是會重新渲染,這時候就是PureComponent內部做了優化的體現。同理也適用於stringnumber等基本數據類型,因爲基本數據類型,值改變了就算改變了。

小結:如果傳值都是簡單對象,可以放心使用;如果傳值有複雜對象,需要慎重使用。

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時,初始化依舊是輸出constructorrender,但是當點擊按鈕時,界面上沒有變化,也沒有輸出render,證明沒有渲染,但是我們可以從下面的註釋中看到,每點擊一次按鈕,我們想要修改的arr的值已經改變,而這個值將去修改this.state.arr,但是因爲在PureComponent淺比較這個數組的引用沒有變化所以沒有渲染,this.state.arr也沒有更新,因爲在this.setState()以後,值是在render的時候更新的。

小結:如果是複雜數據類型,這裏會造成錯誤的顯示(setState淺複製更新,但是界面不會重新渲染)

6.但是當這個組件是繼承自Component的時候,初始化依舊是輸出constructorrender,但是當點擊按鈕時,界面上出現了變化,即我們打印處理的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的引用發生了變化,所以初始化的時候輸出constructorrender後,每次點擊按鈕都會輸出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時:下面的結果初始化時輸出爲constructorIndexPage renderexample render,但是當我們點擊按鈕時,界面沒有變化,因爲這個this.state.person對象的引用沒有改變,只是改變了它裏面的屬性值所以儘管子組件是繼承Component的也沒有辦法渲染,因爲父組件是PureComponent,父組件根本沒有渲染,所以子組件也不會渲染

3.父組件繼承PureComponent,子組件繼承PureComponent時:因爲渲染在父組件的時候就沒有進行,相當於被攔截了,所以子組件是PureComponent還是Component根本不會影響結果,界面依舊沒有變化

4.父組件繼承Component,子組件繼承PureComponent時:結果和我們預期的一樣,即初始化是會輸出constructorIndexPage renderexample render,但是點擊的時候只會出現IndexPage render,因爲父組件是Component,所以父組件會渲染,但是
當父組件把值傳給子組件的時候,因爲子組件是PureComponent,所以它會對prop進行淺比較,發現這個person對象的引用沒有發生變化,所以不會重新渲染,而界面顯示是由子組件顯示的,所以界面也不會變化

5.父組件繼承Component,子組件繼承Component時:初始化是會輸出constructorIndexPage renderexample render,當我們第一次點擊按鈕以後,界面發生變化,後面就不再改變,因爲我們一直把它設置爲sxt2,但是每點擊一次都會輸出IndexPage renderexample render,因爲每次不管父組件還是子組件都會渲染

6.所以正如下面第四條說的,如果stateprop一直變化的話,還是建議使用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的組件不用我們手動去判斷propstate,所以在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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章