React 效能優化總結
總結了以下幾個方面在react上的效能優化
-
常見的效能問題場景
-
時刻注意程式碼的潛在效能問題
-
注意可重構的程式碼,元件化
-
瞭解如何使用工具定位效能問題
常見的效能問題場景
-
JavaScript 語言採用的是單執行緒模型,也就是說,所有任務只能在一個執行緒上完成,一次只能做一件事。如果頁面比較複雜,新增了大量的計算,並且還新增了Canvas(Canvas 是一個非常受歡迎的表現方式,同時也是WebGL的入口。它能繪製圖形,圖片,展示動畫,甚至是處理視訊內容)的繪製,那頁面載入可能會卡頓,有可能會呈現出假死狀態。
解決方案:在Worker中使用OffscreenCanvas或者將頁面渲染時大量的計算放在Worker中。 首先我們先了解幾個概念(以在Worker中用OffscreenCanvas為列)
-
Workers 是一個Web版的執行緒——它允許你在幕後執行你的程式碼。將你的一部分程式碼放到Worker中可以給你的主執行緒更多的空閒時間,這可以提高你的使用者體驗度
-
OffscreenCanvas並不依賴DOM。
- 一種是在 Worker 執行緒建立一個 OffscreenCanvas 做後臺渲染,然後再把渲染好的緩衝區 Transfer 回主執行緒顯示。
- 一種是主執行緒從當前 DOM 樹中的 Canvas 元素產生一個 OffscreenCanvas,再把這個 OffscreenCanvas 傳送給 Worker 執行緒進行渲染,渲染的結果直接 Commit 到瀏覽器的 Display Compositor 輸出到當前視窗,相當於在 Worker 執行緒直接更新 Canvas 元素的內容。
- 一種是在 Worker 執行緒建立一個 OffscreenCanvas 做後臺渲染,然後再把渲染好的緩衝區 Transfer 回主執行緒顯示。
-
OffscreenCanvas學習文件。
另外web workers學習文件請去官網瞭解總結: 學習成本比較低,在耗效能的計算和渲染放在Worker中,確實能提升使用者體驗度
-
-
dom節點層次多,而且深,更改state或者更改redux,導致與該資料無相關的dom節點多次render。
-
js處理資料過於複雜。定義的狀態資料層次過於深。導致對比或者遍歷資料消耗效能。
時刻注意程式碼的潛在效能問題
-
{...this.props} 不要濫用,請只傳遞component需要的props,傳得太多,或者層次傳得太深,都會加重shouldComponentUpdate裡面的資料比較負擔。
-
方法繫結的使用
- 方法直接繫結在dom節點中
<div onClick={this.tap.bind(this)} /> 複製程式碼
- 方法繫結放在constructor中
constructor(props) { super(props); this.tap= this.tap.bind(this); } 複製程式碼
- 箭頭函式
tap = ()=>{}; <div onClick={this.tap} /> 複製程式碼
tap =(value)=> {}; <div onClik={()=>this.tap(value)} /> 複製程式碼
總結:
1.由於繫結是在render中執行,而render是會執行多次的,每次bind和箭頭函式都會產生一個新的函式,因而帶來了額外的開銷
2.使用構造器bind的方法,每個例項都可以有效地共享函式體,從而更加有效的使用記憶體。但當要繫結的函式較多時,這種方法又顯得相對的枯燥和無聊。所以,在知道例項不多,函式體不大的前提下,使用箭頭函式更加快捷。
3.綜合三種寫法,第三種是目前最優寫法 -
陣列遍歷map
map裡面新增key,並且key不要使用index(可變的),儘量使用穩定常量作為key。使用index作為key,只是會讓程式碼不報錯,其他一無是處。 每當元件的props或state改變時, React會重新建立一個virtual DOM, 與上一個作對比, 如果發現兩個virtual DOM不完全相同, 則React就會做reconcile, 把有差異的地方更新到真實的DOM上。 使用常量作為key 複製程式碼
-
儘量少用不可控的refs、DOM操作。
-
props和state的資料儘可能簡單明瞭,扁平化。便於資料對比,陣列遍歷從而減小帶來的效能消耗。
-
使用return null而不是CSS的display:none來控制節點的顯示隱藏。保證同一時間頁面的DOM節點儘可能的少。
注意可重構的程式碼,元件化
- 元件分類
- 可複用性
- 足夠細
- 耦合度低
元件分類
在開發前期可以根據業務場景將元件分類
- 展示類元件(沒有任何互動,只是純展示資料)
- 互動類元件(頁面互動操作比較頻繁)
- 資料類元件(比如dva,redux,基本在搭建框架時已模組化)
- 高階元件(對原有元件的保護,更利於後續的迭代開發)
可複用性
這裡我們就要說一下有狀態元件和無狀態元件的區別了 就如上所說的展示類元件,這種我們就可以把它歸類為無狀態元件,舉個列子
import React from 'react';
const PicModal = props => {
const { title = '', content = '', picUrl = '', deviceName = '' } = props;
return (
<div>
<span>title</span>
<span>content</span>
<span>deviceName</span>
{picUrl !== '' ? <img src={picUrl}/> : null}
</div>
);
};
export default PicModal;
複製程式碼
這樣我們就可以自己封裝一個modal元件,根據業務場景不斷的迭代優化。
而上面的互動類元件大多數情況下都是有狀態元件,維護自身的狀態值。但是要實現可複用性,元件之間的耦合度肯定是要低的。元件自己控制自己內部的state。這樣setState只用區域性更新檢視。減小效能消耗。
注意:千萬不要在父元件定義state,傳值給子元件。
1.降低元件之間的耦合度
2.便於後續的元件迭代
足夠細
根據業務場景將元件拆分的足夠細。
- 可讀性強
- 維護性高
- 便於複用
耦合度低
下面舉一個移動端的列子,web端大多數都會有這種情況,點選專案id列表中projectId,projectList改變。
解決方案:元件1和元件2是通過projectId進行聯動。如果我們拿到這個需求,首先元件化1和2,元件1通過點選id,在父元件中執行getProjectList方法,從而實現渲染元件2。根據元件1中id的state渲染元件1
這樣父元件與子元件耦合度很低,子元件自己維護自己的狀態值
兄弟元件之間互不影響,通過父元件通訊
如果拆分業務元件的思路不清晰,盲目的將狀態值放在父元件中,這樣耦合度會大大增加,元件的複用性會大大降低。
瞭解如何使用工具定位效能問題
- chrome外掛 redux devtools
- chrome外掛 react devtools