React 效能最佳化,你需要知道的幾個點
寫了一段時間的
react
之後,漸漸的喜歡上了使用react
來寫應用。我們知道,
react
時打出的旗號之一就是高效能。今天我們還一起來聊一聊
react
的效能最佳化,思考還能透過哪些手段來提升React的效能,使我們的react
更快,效能更好。
一,react元件的效能最佳化(渲染角度最佳化)
1,react效能檢視工具
再講效能最佳化之前,我們需要先來了解一下如何檢視react載入元件時所耗費的時間的工具,在react 16版本之前我們可以使用React Perf
來檢視。
大家可以在chorme中先安裝React Perf擴充套件,然後在入口檔案或者redux
的store.js
中加入相應的程式碼即可:
在最新的React16版本中,我們可以直接在url後加上?react_pref
,就可以在chrome瀏覽器的performance
,我們可以檢視User Timeing
來檢視元件的載入時間。
react16.0_pref
使用此工具的具體操作大家可以看下圖:
react16.0_pref.gif
2,單個react元件效能最佳化
2.1,render
裡面儘量減少新建變數和bind
函式,傳遞引數是儘量減少傳遞引數的數量。
首先我們先思考一個問題,比如我要實現一個點選按鈕使相應的num
增加1,我們有哪一些方法。
大家應該都能想到,無非就是三種,如下圖:
react_function
第一種是在建構函式中繫結this
,第二種是在render()
函式里面繫結this
,第三種就是使用箭頭函式,都能實現上述方法;
但是哪一種方法的效能最好,是我們要考慮的問題。應該大家都知道答案:第一種的效能最好。
因為第一種,建構函式每一次渲染的時候只會執行一遍;
而第二種方法,在每次render()
的時候都會重新執行一遍函式;
第三種方法的話,每一次render()
的時候,都會生成一個新的箭頭函式,即使兩個箭頭函式的內容是一樣的。
第三種方法我們可以舉一個例子,因為react
判斷是否需要進行render
是淺層比較,簡單來說就是透過===
來判斷的,如果state
或者prop
的型別是字串或者數字,只要值相同,那麼淺層比較就會認為其相同;
但是如果前者的型別是複雜的物件的時候,我們知道物件是引用型別,淺層比較只會認為這兩個prop
是不是同一個引用,如果不是,哪怕這兩個物件中的內容完全一樣,也會被認為是兩個不同的prop
。
舉個例子:
當我們給元件Foo
給名為style
的prop
賦值;
使用這種方法,每一次渲染都會被認為是一個
style
這個prop
發生了變化,因為每一次都會產生一個物件給style
。那麼我們應該如何改進,如果想要讓
react
渲染的時候認為前後物件型別prop
相同,則必須要保證prop
指向同一個javascript
物件,如下:const fooStyle = { color: "red" }; //取保這個初始化只執行一次,不要放在render中,可以放在建構函式中這個問題是我們在平時的編碼中可以避免的。
2.2,定製
shouldComponentUpdate
函式
shouldComponentUpdate
是決定react
元件什麼時候能夠不重新渲染的函式,但是這個函式預設的實現方式就是簡單的返回一個true
。也就是說,預設每次更新的時候都要呼叫所用的生命週期函式,包括render
函式,重新渲染。我們來看一下下面的一個例子
shouldComponentUpdate
我們寫兩個元件,
App
和Demo
元件,並寫兩個方法,一個改變App
中的num
的值,一個是改變title
,我們在Demo的render中列印render函式。我們可以看到以下的效果:我們可以清晰的看到雖然
demo
元件裡的title
值沒有改變,但是還是render
了。為了解決這個問題,我們可以對demo元件進行如下的修改:
只有當demo的title值發生改變的時候,我們才去render,我們可以看一下效果:
以上只是一個特別簡單的一個對於
shouldComponentUpdate
的定製。在最新的
react
中,react給我們提供了React.PureComponent
,官方也在早期提供了名為react-addons-pure-render-mixin
外掛來重新實現shouldComponentUpdate
生命週期方法。
透過上述的方法的效果也是和我們定製
shouldComponentUpdate
的效果是一致的。但是我們要注意的是,這裡的
PureRender
是淺比較的,因為深比較的場景是相當昂貴的。所以我們要注意我們在1.1
中說到的一些注意點:不要直接為props設定物件或者陣列、不要將方法直接繫結在元素上,因為其實函式也是物件
2.3,Immutable.js
Shared mutable state is the root of all evil(共享的可變狀態是萬惡之源)
-- Pete Hunt
javascript
中的物件一般都是可變的,因為使用了引用賦值,新的物件簡單的引用了原始物件,改變新物件將影響到原始物件。舉個例子:
foo = { a : 1 }; bar = foo; bar.a = 2;當我們給
bar.a
賦值後,會發現foo.a
也變成了2,雖然我們可以透過深複製與淺複製解決這個問題,但是這樣做非常的昂貴,對cpu
和記憶體會造成浪費。這裡就需要用到
Immutable
,透過Immutable
建立的Immutable Data
一旦被建立,就不能再更改。對Immutable
物件進行修改、新增或刪除操作,都會返回一個新的Immutable
物件。這裡我們將一下其中三個比較重要的資料結構
Map:鍵值對集合,對應
Object
,Es6
種也有專門的Map
物件List:有序可重複列表,對應於
Array
ArraySet:有序且不可重複的列表
我們可以看兩個例子:
使用
Map
生成一個immutable
物件import { Map , is } from 'immutable';let obj = Map({ 'name': 'react study', 'course': Map({name: 'react+redux'}) })let obj1 = obj.set('name','darrell');console.log(obj.get('course') === obj1.get('course')); // 返回trueconsole.log(obj === obj1); // 返回false
Immutable.is
比較的是兩個物件的hashCode
或valueOf
(對於 JavaScript 物件)。由於 immutable 內部使用了 Trie 資料結構來儲存,只要兩個物件的hashCode
相等,值就是一樣的。這樣的演算法避免了深度遍歷比較,效能非常好。let obj = Map({name:1,title:'react'});let obj1 = Map({name:1,title:'react'});console.log(is(obj,obj1)); // 返回truelet obj2 = {name:1,title:'react'};let obj3 = {name:1,title:'react'};console.log(is(obj2,obj3)); // 返回false
Immutable
優點:
減少記憶體的使用
併發安全
降低專案的複雜度
便於比較複雜資料,定製shouldComponentUpdate方便
時間旅行功能
函數語言程式設計
Immutable
缺點:
學習成本
庫的大小(建議使用seamless-immutable)
對現有專案入侵嚴重
容易與原生的物件進行混淆
如果大家想深入瞭解,可以參考、。
2.4,多個react元件效能最佳化,key的最佳化
react
元件在裝載過程中,react
透過在render
方法在記憶體中產生一個樹形結構,樹上的節點代表一個react
元件或者原生的Dom
元素,這個樹形結構就是我們所謂的Vitural Dom
,react根據這個來渲染產生瀏覽器的Dom
樹。
react
在更新階段對比原有的Vitural Dom
和新生成的Vitural Dom
,找出不同之處,在根據不同來渲染Dom樹。react為了追求高效能,採用了時間複雜度為
O(N)
來比較兩個屬性結構的區別,因為要確切比較兩個樹形結構,需要透過O(N^3)
,這會降低效能。我們舉幾個情況,大家就會馬上理解:
節點型別不同
// A元件// B元件我們想把
A
元件更新成B
元件,react
在做比較的時候,發現最外面的根結點不一樣,直接就廢掉了之前的
節點,包括裡面的子節點,這是一個巨大的浪費,但是為了避免O(N^3)
的時間複雜度,只能採用這種方式所以在開發過程中,我們應該儘量避免上面的情況,不要將包裹節點的型別隨意改變。
兩個節點型別一樣
這裡包括兩種情況,一種是節點是
Dom
型別,還有一種react
元件。對於
dom
型別,我們舉個例子:// A元件Hello World!!!// B元件Good Bye!!!上述A和B元件的區別是文字、
className
、style
中的color
發生改變,因為Dom
元素沒變,React
只會修改他變化的部分。針對
react
元件型別,渲染無非就是在走一遍元件例項的更新過程,最主要的就是定製shouldComponentUpdate
,我們上面也有講到,就不細講了。多個子元件情況
我們看兩個例子就能明白
例子一:
// A// B
從A變到B,如果
shouldComponentUpdate
處理得當,我們只需要更新裝載third
的那一次就行。我們來看看下一個例子:
// A// B
這裡因為react是採用O(n)的時間複雜度,所以會依次將text為First的改為Zero,text為Second改為First,在最後再加上一個元件,text為Second。現存的兩個的text的屬性都被改變了,所以會依次渲染。
如果我們這裡有1000個例項,那麼就會發生1000次更新。
這裡我們就要用到
Key
了簡單來說,其實這一個Key就是react元件的身份證號。
我們將上一個例子改成如下,就可以避免上面的問題了,react就能夠知道其實B裡面的第二個和第三個元件其實就是A中的第一個和第二個例項。
// A// B
不過現在,react也會提醒我們不要忘記使用
key
,如果沒有加,在瀏覽器中會報錯。
關於
key
的使用我們要注意的是,這個key值要穩定不變的,就如同身份證號之於我們是穩定不變的一樣。一個常見的錯誤就是,拿陣列的的下標值去當做key,這個是很危險的,程式碼如下,我們一定要避免。
{ todos.map((item, index) => {
二,redux效能最佳化:reselect(資料獲取時最佳化)
在前面的最佳化過程中,我們都是最佳化渲染來提高效能的,既然
react
和redux
都是透過資料驅動的的方式驅動渲染過程,那麼處理最佳化渲染過程,獲取資料的過程也是需要考慮的一個最佳化點。//下面是redux中簡單的一個篩選功能const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } }const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
mapStateToProps
函式作為redux store
中獲取資料的重要一環,當我們根據filter
和todos
來顯示相應的待辦事項的時候,我們都要遍歷todos
欄位上的陣列。當陣列比較大的時候,則會降低效能。
這個時候,reselect就應運而生了,它的動作原理:只要相關的狀態沒有改變,那麼就直接使用上一次的快取結果。
具體的用法我就不在這裡過多介紹了,已經有很多的牛人寫了相關的文章,我也不重複寫了,大家如果想深入瞭解的話,可以參考、。
三:參考資料
此篇文章是參考了《深入React技術棧》和《深入淺出React與Redux》這兩本書中關於對react效能最佳化的章節,再加上自己的動手實踐與思考寫的。
文章中不乏會有些錯誤的地方還請大家多多批評指正。
作者:darrell
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4650/viewspace-2802968/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python的五個特點,你知道幾個?Python
- 使用Mac便箋?你需要知道的幾個快捷鍵Mac
- SEO最佳化網站診斷的幾個技巧,你知道多少?網站
- Linux系統的六大特點,你知道幾個?Linux
- 製作電商主圖前需要知道的幾個要點!
- Spring中11個最常用的擴充套件點,你知道幾個?Spring套件
- 學習swoole之前,你需要知道的幾件事
- 入門Java你需要了解的幾個知識要點!Java
- 你需要知道的幾類npm依賴包管理NPM
- 關於Mysql事務,你必須知道的幾個知識點!MySql
- 你知道黑客的入侵方式都有哪些嗎?這些你知道幾個?黑客
- 效能測試需要知道點系統概念
- 關於redis,你需要了解的幾點!Redis
- 學習 React 前你需要知道些什麼React
- 【譯】10 個你不知道你需要的 HTML 元素HTML
- TQM的八項原則,你知道幾個?
- 談一談你知道的前端效能最佳化方案有哪些?前端
- 軟體測試的五個目的,你知道幾個?
- 學習React之前你需要知道的的JavaScript基礎知識ReactJavaScript
- 8個Python爬蟲框架,你知道幾個?Python爬蟲框架
- Spring事務需要注意的幾個點Spring
- 編寫Spark程式的幾個最佳化點Spark
- Netty中的這些知識點,你需要知道!Netty
- 你知道嗎?一個好的ERP系統需要具備這些特點
- 直播系統原始碼的收益方式你知道幾點?原始碼
- 你需要知道的kafkaKafka
- 你需要知道的SymbolsSymbol
- es6 export ,這幾點你知道嘛Export
- 這幾個python常用的庫你必須知道!Python
- 目前主流的內網穿透方式 你知道幾個內網穿透
- LLM部署,你必須要知道的幾個技巧!
- Laravel 你應該知道的幾個最佳實踐Laravel
- 榮耀9隱藏的3個功能 你知道幾個?
- 十個python熱門專案,你知道幾個Python
- 7個關於"this"面試題,你知道幾個?面試題
- 雲伺服器建站的幾點好處,你知道嗎?伺服器
- Linux伺服器有哪些防護要點?這幾個你知道嗎?Linux伺服器
- 2018年你需要知道的13個JavaScript工具庫JavaScript