React性能優化:PureComponent的使用原則

首先歡迎你們關注個人Github博客,也算是對個人一點鼓勵,畢竟寫東西無法得到變現,能堅持下去也是靠的是本身的熱情和你們的鼓勵,但願你們多多關注呀!react的部分持續更新中...react

React15.3中新加了一個PureComponent類,取代以前的PureRenderMixin , PureComponent能夠進行React性能優化,減小沒必要要的render渲染次數,使用時只要把繼承類從Component換成PureComponent。git

PureComponent的原理是繼承了Component類,自動加載shouldComponentUpdate函數,當組件更新時,shouldComponentUpdate對props和state進行了一層淺比較,若是組件的props和state都沒有發生改變,render方法就不會觸發,省去Virtual DOM的生成和對比過程,達到提高性能的目的。github

下面從PureComponent的使用和源碼來完全瞭解它.npm

shouldComponentUpdate優化性能

在PureComponent以前,咱們常常看到優化react性能最多見的手段之一就是在react的生命週期函數shouldComponentUpdate裏判斷props或state的數據是否發生變化,經過返回ture(更新)和false(不更新)來阻止沒必要要的render.瀏覽器

首先來看看react產生沒必要要渲染的一個場景:性能優化

用create-react-app來初始化react的運行環境,而後在App.js文件裏建立一個定時任務,每隔一秒更新數據:app

import React, { Component } from 'react'
import ShouldComponentUpdateList from './ShouldComponentUpdateList'

// 容器組件
export default class App extends Component {
    state = {
        data: []
    }
    componentDidMount() {
        // 定時任務,每隔一秒更新數據
        setInterval(() => {
            this.setState({
                data: [
                    { title: 'react line 1' }, 
                    { title: 'react line 2' }, 
                ]
            })
        }, 1000)
    }
    render() {
        return(
            <div> { this.state.data.map((item, index) => ( <ShouldComponentUpdateList key={index} list={item} /> )) } </div> ) } } 複製代碼

ShouldComponentUpdateList組件內容爲:frontend

export default class List extends Component {
    render() {
        console.log('list render')
        return(
            <div>{this.props.list.title}</div>
        )
    }
}
複製代碼

命令行運行npm start,瀏覽器查看輸出:函數

發現控制檯每隔一秒都會輸出list render,明明數據沒有發生變化,可是react仍是發生渲染,形成了沒必要要的渲染浪費。性能

只須要在shuoldComponentUpdate里加上判斷,再次查看輸出結果,定時任務的數據沒有發生改變,不會再渲染render函數:

export default class List extends Component {
    // 在shuoldComponentUpdate裏判斷props傳遞的數據沒有發生變化,則不須要render
    shouldComponentUpdate(nextProps) {
        // 返回值爲true則render,爲false則不render
        if(nextProps.list.title === this.props.list.title) {
            return false
        }
        return true
    }
    render() {
        console.log('list render')
        return(
            <div>{this.props.list.title}</div>
        )
    }
}
複製代碼

PureComponent使用

除了使用shouldComponentUpdate來判斷是否須要更新組件,還能夠用PureComponent, PureComponent實際上自動加載shouldComponentUpdate函數,當組件更新時,shouldComponentUpdate對props和state進行了一層淺比較.

新建PureComponentList組件,用PureComponent代替Component:

import React, { PureComponent } from 'react'
export default class List extends PureComponent {
    render() {
        console.log('list render')
        return(
            <div>{this.props.list.title}</div>
        )
    }
}
複製代碼

在App組件中傳入:

this.state.data.map((item, index) => (
    <PureComponentList key={index} list={item}/> )) 複製代碼

而後查看瀏覽器輸出結果,驚奇地發生,PureComponent並無阻止沒必要要render,這是爲何呢?由於前面咱們說到PureComponent的shouldComponentUpdate只對props和state進行淺比較,也就是this.props = { list: { title: 'react line1' } }nextProps = { list: { title: 'react line1' } },做淺比較的話this.props固然不等於next.props.

爲了更清晰地找到緣由,咱們先來看看PureComponent的源碼.

PureComponent源碼

首先找到PureComponent這個函數,在構造函數和原型上分別繼承了Component的屬性和方法:

export default function PureComponent(props, context) {
    Component.call(this, props, context)
}

PureComponent.prototype = Object.create(Component.prototype)
PureComponent.prototype.constructor = PureComponent
PureComponent.prototype.shouldComponentUpdate = shallowCompare

function shallowCompare(nexProps, nextState) {
    return !shallowEqual(this.props, nextProps) || !shollowEqual(this.state, nextState)
}
複製代碼

接着PureComponent在生命週期函數裏面寫了shallowCompare方法,shallowCompare裏面經過shallowEqual的返回值來返回ture仍是false.

接着來看看shallowEqual函數,源碼地址

export default function shallEqual(objA, objB) {
    // 從後面代碼能夠看出,對於兩個對象的比較爲這裏的代碼
    if (objA === objB) {
        return true;
    }

    if (typeof objA !== 'object' || objA === null ||
        typeof objB !== 'object' || objB === null) {
        return false;
    }

    const keysA = Object.keys(objA);
    const keysB = Object.keys(objB);

    if (keysA.length !== keysB.length) {
        return false;
    }

    // Test for A's keys different from B.
    for (let i = 0; i < keysA.length; i++) {
        if (!objB.hasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
            return false;
        }
    }

    return true;
}
複製代碼

從上面淺比較的源碼shallowEqual函數能夠看出,shallEqual對於對象的比較僅僅經過if (objA === objB) { return true; }來判斷,而let a = { list: { title: 'react line1' } }let b = { list: { title: 'react line1' } }, a === b值爲false,因此這就很好的解釋了上面PureComponent並無阻止沒必要要render的緣由。

因此咱們來改進代碼,使得PureComponent的props若是傳入對象狀況下應該如何起效:

在App.js裏面修改PureComponentList組件傳入item.title而不是item對象,瀏覽器只輸出兩次log:

this.state.data.map((item, index) => (
    // <PureComponentList key={index} list={item}/>
    <PureComponentList key={index} title={item.title}/>
))
複製代碼

經過解構item對象,傳入item.title,這樣就能夠進行淺比較,來達到優化沒必要要渲染的目的.

PureComponent原則

由上面探究PureComponent源碼咱們知道,PureComponent的組件在props或者state的屬性值是對象的狀況下,並不能阻止沒必要要的渲染,是由於自動加載的shouldComponentUpdate裏面作的只是淺比較,因此想要用PureComponent的特性,應該遵照原則:

  • 確保數據類型是值類型
  • 若是是引用類型,不該當有深層次的數據變化(解構).

React.memo

在使用PureComponent的時候,只能把react組件寫成是class的形式,不能使用函數的形式;react v16.6.0以後,可使用React.memo來實現函數式的組件,也有了PureComponent的功能。

List組件的PureComponent:

const ListComponent = React.memo(() => (
    <div>{this.props.data || 'loading'}</div>
))
複製代碼

注:上面涉及到的全部:point_right:源代碼

相關文章
相關標籤/搜索