react如何通過shouldComponentUpdate來減少重複渲染
在react開發中,經常會遇到元件重複渲染的問題,父元件一個state的變化,就會導致以該元件的所有子元件都重寫render,儘管絕大多數子元件的props沒有變化
render什麼時候會觸發
首先,先上一張react生命週期圖:
這張圖將react的生命週期分為了三個階段:生成期、存在期、銷燬期,這樣在create、props、state、unMount狀態變化時我們可以清楚的看到reacte觸發了哪些生命週期鉤子以及什麼時候會render。
如果我們需要更改root的一個state,使綠色元件檢視更改
如果你寫過vue,你會發現元件更新是如上圖那樣的(檢視指令已編譯為修改檢視的函式存放在繫結的state裡的屬性裡,所以能夠做到靶向修改),而react會以元件為根,重新渲染整個元件子樹,如下圖(綠色是期望的render路徑,橙色是無用render):
所以在react裡,我們探討的render效能優化是react呼叫render的路徑如下:
如何避免這些不必要的render:
shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)
使用shouldComponentUpdate()以讓React知道當前狀態或屬性的改變是否不影響元件的輸出,預設返回ture,返回false時不會重寫render,而且該方法並不會在初始化渲染或當使用forceUpdate()時被呼叫,我們要做的只是這樣:
shouldComponentUpdate(nextProps, nextState) {
return nextState.someData !== this.state.someData
}
但是,state裡的資料這麼多,還有物件,還有複雜型別資料,react的理念就是拆分拆分再拆分,這麼多子元件,我要每個元件都去自己一個一個對比嗎??不存在的,這麼麻煩,要知道我們的終極目標是不勞而獲-_-
React.PureComponent
React.PureComponent 與 React.Component 幾乎完全相同,但 React.PureComponent 通過props和state的淺對比來實現 shouldComponentUpate()。如果物件包含複雜的資料結構,它可能會因深層的資料不一致而產生錯誤的否定判斷(表現為物件深層的資料已改變檢視卻沒有更新)
關注點:
- 無論元件是否是 PureComponent,如果定義了 shouldComponentUpdate(),那麼會呼叫它並以它的執行結果來判斷是否 update。在元件未定義 shouldComponentUpdate() 的情況下,會判斷該元件是否是 PureComponent,如果是的話,會對新舊 props、state 進行 shallowEqual 比較,一旦新舊不一致,會觸發 update。
- 淺判等 只會比較到兩個物件的 ownProperty 是否符合 Object.js()判等,不會遞迴地去深層比較---原始碼
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x: mixed, y: mixed): boolean {
// SameValue algorithm
if (x === y) { // Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
// Added the nonzero y check to make Flow happy, but it is redundant
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(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 (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
至於複雜資料結構,用Object.key()獲取下key,然後key和對應的value都是基礎型別資料,就是算是簡單資料結構,不然就是複雜
針對以上規則我們在專案開發種可以做出如下優化:
儘量將複雜型別資料(ArrayList)所關聯的檢視單獨拆成PureComonent有助於提高渲染效能,比如表單、文字域和複雜列表在同一個 render() 中,表單域的輸入欄位改變會頻繁地觸發 setState() 從而導致 元件 重新 render()。而用於渲染複雜列表的資料其實並沒有變化,但由於重新觸發 render(),列表還是會重新渲染。
react-immutable-render-mixin
我想複雜陣列沒變化時也不要render(), 那你用react-immutable-render-mixin,來,我們看看外掛的介紹:
Users are urged to use PureRenderMixin with facebook/immutable-js. If performance is still an issue an examination of your usage of Immutable.js should be your first path towards a solution. This library was created from experimentations with Immutable that were ultimately erroneous; improper usage of Immutable.js. Users should be able to achieve maximum performance simply using PureRenderMixin.
譯:不能以正確的姿勢來使用immutable-js做優化,你就不要瞎折騰了,用它react-immutable-render-mixin就行了
它和ProComponent原理一樣,唯一的區別就是新舊資料的對比,react-immutable-render-mixin用了immutable-js的is()方法去做對比,效能強,複雜型別資料也能對比(這裡不對immutable-js做討論,一篇很不錯的文章Immutable 詳解及 React 中實踐),相比於React.PureComponent更方便---原始碼
import Immutable from 'immutable';
const is = Immutable.is.bind(Immutable);
export default function shallowEqualImmutable(objA, objB) {
if (objA === objB || is(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.
const bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
for (let i = 0; i < keysA.length; i++) {
if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
用法很多,我喜歡Decorator:
資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com
import React from 'react';
import { immutableRenderDecorator } from 'react-immutable-render-mixin';
@immutableRenderDecorator
class Test extends React.Component {
render() {
return <div></div>;
}
}