React性能探索 --- 避免沒必要要渲染

背景

上一篇文章的結尾
http://imweb.io/topic/5985cc4...
咱們說到,也許,不是全部的節點都須要從新渲染,對於那些不須要渲染的節點,咱們如何找到它們並作優化呢?html

本篇文章來具體解答這個問題。react

應用分析

首先,先看這個應用:頁面的兩部分分別渲染5000個節點,從1-5000。當點擊按鈕以後,第二部分的節點會更新,從新渲染從2-5001的數字,可是第一部分保持不變。git

import React, { createElement, Component } from 'react';
import {render} from 'react-dom';
import Perf from 'react-addons-perf';
import ListItem from './ListItem'

function arrayGenerator(length) {
  return Array.apply(null, { length: length }).map(Number.call, Number)
}


class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      multiplier: 1
    }
  }

  resetMultiplier() {
    this.setState({ multiplier: 2 })
  }

  render() {
    return (
      <div className="App">
        <button onClick={this.resetMultiplier.bind(this)}>Click Me</button>
        <ul>
          {
            arrayGenerator(5000).map(i => {
              return <ListItem key={i} text={i}/>
            })
          }
          {
            arrayGenerator(5000).map(i => {
              return <ListItem key={i} text={i + this.state.multiplier}/>
            })
          }
        </ul>
      </div>
    );
  }
}

render(<App />,document.getElementById('main'));

gitbug 連接:
https://github.com/hhhuangqio...github

感興趣的同窗能夠下載跑一跑代碼web

分析更新時間

這裏用react的Perf工具來測量從新渲染的時間。npm

使用方法:redux

npm install --save-dev react-addons-perf
import Perf from 'react-addons-perf'

這裏主要用到四個方法:數組

  • Perf.start():開始計時app

  • Perf.stop():結束計時less

  • Perf.printInclusive():打印組件總的渲染時間

  • Perf.printWasted():打印浪費的時間

當咱們點擊按鈕,能夠看到控制檯打印出下面的信息:

由控制檯的數據能夠看出,App用了90.59ms渲染,其中渲染ListItem的時間爲55ms,渲染了10000次,其中有5000次是浪費的,由於這部分頁面的內容徹底沒有更新的改動。

如何修復

既然是不須要渲染,那就要阻止它的渲染。React給咱們提供了一個方法shouldComponentUpdate(),當這個方法返回true的時候,須要從新渲染,false的時候不須要(默認是true).

在這個栗子中,只要text的值不變,就不須要從新渲染。因此,能夠這樣改寫ListItem 的shouldComponentUpdate

import React, { Component } from 'react'

export default class ListItem extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.text !== this.props.text  
  }

  render() {
    let { text } = this.props
    return <li>{text}</li>
  }
}

在從新點擊一下按鈕,在控制檯能夠發現

App總的渲染時間降到了62.14ms,而且ListItem只從新渲染了5000個節點,徹底消除了浪費的渲染。

對於上面的寫法,React提供了一個新的組件PureComponent來作這件事,它會自動淺對比props/state,當二者相同的時候不渲染節點。因此,listItem又能夠改寫成

import React, { PureComponent } from 'react'

export default class ListItem extends PureComponent {
  render() {
    let { text } = this.props
    return <li>{text}</li>
  }
}

跑一跑代碼

經過控制檯能夠看到達到的效果是同樣的(有點偏差是正常的)。

這裏再安利一個能夠發現應用裏是否存在不應從新渲染的節點工具:why-did-you-update

使用方法

一、npm i --save-dev why-did-you-update
二、
import React from 'react'
if (process.env.NODE_ENV !== 'production') {
  const {whyDidYouUpdate} = require('why-did-you-update')
  whyDidYouUpdate(React)
}

而後點擊按鈕看控制檯


能夠看到Value did not change. Avoidable re-render!的警告,是否是很實用!

注意的點

PureComponent只會淺比較,因此不適合用於深層嵌套的對象。同時,PureComponent不只僅會跳過本身的從新渲染,還會跳過它全部子節點的,因此要注意,用它的時候是最好沒有子節點而且不依賴於global state的展現型組件。

與Staleless的關係

不知道有沒有人跟我有這樣的疑問,無狀態組件跟純淨組件有什麼不一樣?這裏作一個區分:

無狀態組件只是做爲一個展現組件,它的好處是:

  • 易複用,易測試

  • 與邏輯處理數據解耦,通常來講,app裏有越多無狀態組件越好,這說明邏輯處理都在上層,例如redux 中處理,這樣能夠在不渲染的前提下,測數據邏輯。

壞處:

  • 沒有生命週期,沒辦法用shouldComponentUpdate阻止從新渲染,這也就是說,它沒有幫助咱們提升性能的做用,這也是它跟PureComponent最大的不一樣。

關於如何在實際中使用這兩個組件,還要根據具體的實際狀況來選擇~

總結

綜上能夠看出,減小沒必要要的從新渲染對於提高咱們的性能有很大的意義。我我的以爲,在實際中,用Perf跟why-did-you-update兩個工具已經能夠很好幫咱們判斷哪部分不須要從新渲染,幫助咱們作出優化。

遺留點

PureComponent那麼好用,可是使用PureComponent是有條件的呀~

因爲PureComponent只是作了一個淺比較,因此深層嵌套的對象跟數組都是比不出來的,可能會致使須要渲染的地方沒有從新渲染的錯誤展現。

那麼淺比較又是什麼呢?下篇文章咱們來繼續探索

參考連接:

一、https://60devs.com/pure-compo...

二、https://engineering.musefind....

相關文章
相關標籤/搜索