自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React性能優(yōu)化總結(jié)

開發(fā) 前端
本文主要對(duì)在React應(yīng)用中可以采用的一些性能優(yōu)化方式做一下總結(jié)整理

[[419955]]

前言

目的

目前在工作中,大量的項(xiàng)目都是使用react來進(jìn)行開展的,了解掌握下react的性能優(yōu)化對(duì)項(xiàng)目的體驗(yàn)和可維護(hù)性都有很大的好處,下面介紹下在react中可以運(yùn)用的一些性能優(yōu)化方式;

性能優(yōu)化思路

對(duì)于類式組件和函數(shù)式組件來看,都可以從以下幾個(gè)方面去思考如何能夠進(jìn)行性能優(yōu)化

  • 減少重新render的次數(shù)
  • 減少渲染的節(jié)點(diǎn)
  • 降低渲染計(jì)算量
  • 合理設(shè)計(jì)組件

減少重新render的次數(shù)

在react里時(shí)間耗時(shí)最多的一個(gè)地方是reconciliation(reconciliation 的最終目標(biāo)是以最有效的方式,根據(jù)新的狀態(tài)來更新 UI,我們可以簡(jiǎn)單地理解為 diff),如果不執(zhí)行render,也就不需要reconciliation,所以可以看出減少render在性能優(yōu)化過程中的重要程度了。

PureComponent

React.PureComponent 與 React.Component 很相似。兩者的區(qū)別在于 React.Component 并未實(shí)現(xiàn) shouldComponentUpdate(),而 React.PureComponent 中以淺層對(duì)比 prop 和 state 的方式來實(shí)現(xiàn)了該函數(shù)。

需要注意的是在使用PureComponent的組件中,在props或者state的屬性值是對(duì)象的情況下,并不能阻止不必要的渲染,是因?yàn)樽詣?dòng)加載的shouldComponentUpdate里面做的只是淺比較,所以想要用PureComponent的特性,應(yīng)該遵守原則:

  • 確保數(shù)據(jù)類型是值類型
  • 如果是引用類型,不應(yīng)當(dāng)有深層次的數(shù)據(jù)變化(解構(gòu))

ShouldComponentUpdate

可以利用此事件來決定何時(shí)需要重新渲染組件。如果組件 props 更改或調(diào)用 setState,則此函數(shù)返回一個(gè) Boolean 值,為true則會(huì)重新渲染組件,反之則不會(huì)重新渲染組件。

在這兩種情況下組件都會(huì)重新渲染。我們可以在這個(gè)生命周期事件中放置一個(gè)自定義邏輯,以決定是否調(diào)用組件的 render 函數(shù)。

下面舉一個(gè)小的例子來輔助理解下:

比如要在你的應(yīng)用中展示學(xué)生的詳細(xì)資料,每個(gè)學(xué)生都包含有多個(gè)屬性,如姓名、年齡、愛好、身高、體重、家庭住址、父母姓名等;在這個(gè)組件場(chǎng)景中,只需要展示學(xué)生的姓名、年齡、住址,其他的信息不需要在這里展示,所以在理想情況下,除去姓名、年齡、住址以外的信息變化組件是不需要重新渲染的;

示例代碼如下:

  1. import React from "react"
  2.  
  3. export default class ShouldComponentUpdateUsage extends React.Component { 
  4.  
  5.   constructor(props) { 
  6.     super(props); 
  7.     this.state = { 
  8.       name: "小明"
  9.       age: 12
  10.       address: "xxxxxx"
  11.       height: 165
  12.       weight: 40 
  13.     } 
  14.   } 
  15.  
  16.   componentDidMount() { 
  17.     setTimeout(() => { 
  18.       this.setState({ 
  19.         height: 168
  20.         weight: 45 
  21.       }); 
  22.     }, 5000
  23.   } 
  24.  
  25.   shouldComponentUpdate(nextProps, nextState) { 
  26.       if(nextState.name !== this.state.name || nextState.age !== this.state.age || nextState.address !== this.state.address) { 
  27.         return true
  28.       } 
  29.       return false
  30.   } 
  31.  
  32.   render() { 
  33.     const { name, age, address } = this.state; 
  34.     return ( 
  35.       <div> 
  36.         <p>Student name: {name} </p> 
  37.         <p>Student age:{age} </p> 
  38.         <p>Student address:{address} </p> 
  39.       </div> 
  40.     ) 
  41.   } 

按照 React 團(tuán)隊(duì)的說法,shouldComponentUpdate是保證性能的緊急出口,既然是緊急出口,那就意味著我們輕易用不到它。但既然有這樣一個(gè)緊急出口,那說明有時(shí)候它還是很有必要的。所以我們要搞清楚到底什么時(shí)候才需要使用這個(gè)緊急出口。

使用原則

當(dāng)你覺得,被改變的state或者props,不需要更新視圖時(shí),你就應(yīng)該思考要不要使用它。

需要注意的一個(gè)地方是:改變之后,又不需要更新視圖的狀態(tài),也不應(yīng)該放在state中。

shouldComponentUpdate的使用,也是有代價(jià)的。如果處理得不好,甚至比多render一次更消耗性能,另外也會(huì)使組件的復(fù)雜度增大,一般情況下使用PureComponent即可;

React.memo

如果你的組件在相同 props 的情況下渲染相同的結(jié)果,那么你可以通過將其包裝在 React.memo 中調(diào)用,以此通過記憶組件渲染結(jié)果的方式來提高組件的性能表現(xiàn)。這意味著在這種情況下,React 將跳過渲染組件的操作并直接復(fù)用最近一次渲染的結(jié)果。

React.memo 僅檢查 props 變更 。如果函數(shù)組件被 React.memo 包裹,且其實(shí)現(xiàn)中擁有 useState,useReducer 或 useContext 的 Hook,當(dāng) state 或 context 發(fā)生變化時(shí),它仍會(huì)重新渲染。

默認(rèn)情況下其只會(huì)對(duì)復(fù)雜對(duì)象做淺層對(duì)比,如果你想要控制對(duì)比過程,那么請(qǐng)將自定義的比較函數(shù)通過第二個(gè)參數(shù)傳入來實(shí)現(xiàn)。

  1. function MyComponent(props) { 
  2.   /* 使用 props 渲染 */ 
  3. function areEqual(prevProps, nextProps) { 
  4.   /* 
  5.   如果把 nextProps 傳入 render 方法的返回結(jié)果與 
  6.   將 prevProps 傳入 render 方法的返回結(jié)果一致則返回 true, 
  7.   否則返回 false 
  8.   */ 
  9. export default React.memo(MyComponent, areEqual); 

注意

與 class 組件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 會(huì)返回 true;如果 props 不相等,則返回 false。這與 shouldComponentUpdate 方法的返回值相反。

合理使用Context

Context 提供了一個(gè)無需為每層組件手動(dòng)添加 props,就能在組件樹間進(jìn)行數(shù)據(jù)傳遞的方法。正是因?yàn)槠溥@個(gè)特點(diǎn),它是可以穿透React.memo或者shouldComponentUpdate的比對(duì)的,也就是說,一旦 Context 的 Value 變動(dòng),所有依賴該 Context 的組件會(huì)全部 forceUpdate.這個(gè)和 Mobx 和 Vue 的響應(yīng)式系統(tǒng)不同,Context API 并不能細(xì)粒度地檢測(cè)哪些組件依賴哪些狀態(tài)。

原則

  • Context中只定義被大多數(shù)組件所共用的屬性,例如當(dāng)前用戶的信息、主題或者選擇的語言。

避免使用匿名函數(shù)

首先來看下下面這段代碼

  1. const MenuContainer = ({ list }) => ( 
  2.   <Menu> 
  3.     {list.map((i) => ( 
  4.       <MenuItem key={i.id} onClick={() => handleClick(i.id)} value={i.value} /> 
  5.     ))} 
  6.   </Menu> 
  7. ); 

上面這個(gè)寫法看起來是比較簡(jiǎn)潔,但是有一個(gè)潛在問題是匿名函數(shù)在每次渲染時(shí)都會(huì)有不同的引用,這樣就會(huì)導(dǎo)致Menu組件會(huì)出現(xiàn)重復(fù)渲染的問題;可以使用useCallback來進(jìn)行優(yōu)化:

  1. const MenuContainer = ({ list }) => { 
  2.   const handleClick = useCallback( 
  3.     (id) => () => { 
  4.       // ... 
  5.     }, 
  6.     [], 
  7.   ); 
  8.  
  9.   return ( 
  10.     <Menu> 
  11.       {list.map((i) => ( 
  12.         <MenuItem key={i.id} id={i.id} onClick={handleClick(i.id)} value={i.value} /> 
  13.       ))} 
  14.     </Menu> 
  15.   ); 
  16. }; 

減少渲染的節(jié)點(diǎn)

組件懶加載

組件懶加載可以讓react應(yīng)用在真正需要展示這個(gè)組件的時(shí)候再去展示,可以比較有效的減少渲染的節(jié)點(diǎn)數(shù)提高頁面的加載速度

React官方在16.6版本后引入了新的特性:React.lazy 和 React.Suspense,這兩個(gè)組件的配合使用可以比較方便進(jìn)行組件懶加載的實(shí)現(xiàn);

React.lazy

該方法主要的作用就是可以定義一個(gè)動(dòng)態(tài)加載的組件,這可以直接縮減打包后bundle的體積,并且可以延遲加載在初次渲染時(shí)不需要渲染的組件,代碼示例如下:

使用之前

  1. import SomeComponent from './SomeComponent'

使用之后

  1. const SomeComponent = React.lazy(() => import('./SomeComponent')); 

使用 React.lazy 的動(dòng)態(tài)引入特性需要 JS 環(huán)境支持 Promise。在 IE11 及以下版本的瀏覽器中需要通過引入 polyfill 來使用該特性。

React.Suspense

該組件目前主要的作用就是配合渲染lazy組件,這樣就可以在等待加載lazy組件時(shí)展示loading元素,不至于直接空白,提升用戶體驗(yàn);

Suspense組件中的 fallback 屬性接受任何在組件加載過程中你想展示的 React 元素。你可以將 Suspense 組件置于懶加載組件之上的任何位置,你甚至可以用一個(gè) Suspense 組件包裹多個(gè)懶加載組件。

代碼示例如下:

  1. import React, { Suspense } from 'react'
  2.  
  3. const OtherComponent = React.lazy(() => import('./OtherComponent')); 
  4. const AnotherComponent = React.lazy(() => import('./AnotherComponent')); 
  5.  
  6. function MyComponent() { 
  7.   return ( 
  8.     <div> 
  9.       <Suspense fallback={<div>Loading...</div>}> 
  10.         <section> 
  11.           <OtherComponent /> 
  12.           <AnotherComponent /> 
  13.         </section> 
  14.       </Suspense> 
  15.     </div> 
  16.   ); 

有一點(diǎn)要特別注意的是:React.lazy 和 Suspense 技術(shù)還不支持服務(wù)端渲染。如果你想要在使用服務(wù)端渲染的應(yīng)用中使用,推薦使用 Loadable Components 這個(gè)庫,可以結(jié)合這個(gè)文檔 服務(wù)端渲染打包指南 來進(jìn)行查看。

另外在業(yè)內(nèi)也有一些比較成熟的react組件懶加載開源庫: react-loadable 和 react-lazyload ,感興趣的可以結(jié)合看下;

虛擬列表

虛擬列表是一種根據(jù)滾動(dòng)容器元素的可視區(qū)域來渲染長列表數(shù)據(jù)中某一個(gè)部分?jǐn)?shù)據(jù)的技術(shù),在開發(fā)一些項(xiàng)目中,會(huì)遇到一些不是直接分頁來加載列表數(shù)據(jù)的場(chǎng)景,在這種情況下可以考慮結(jié)合虛擬列表來進(jìn)行優(yōu)化,可以達(dá)到根據(jù)容器元素的高度以及列表項(xiàng)元素的高度來顯示長列表數(shù)據(jù)中的某一個(gè)部分,而不是去完整地渲染長列表,以提高無限滾動(dòng)的性能。

可以關(guān)注下放兩個(gè)比較常用的類庫來進(jìn)行深入了解

  • react-virtualized
  • react-window

降低渲染計(jì)算量

useMemo

先來看下useMemo的基本使用方法:

  1. function computeExpensiveValue(a, b) { 
  2.   // 計(jì)算量很大的一些邏輯 
  3.   return xxx 
  4.  
  5. const memoizedValue = useMemo(computeExpensiveValue, [a, b]); 

useMemo 的第一個(gè)參數(shù)就是一個(gè)函數(shù),這個(gè)函數(shù)返回的值會(huì)被緩存起來,同時(shí)這個(gè)值會(huì)作為 useMemo 的返回值,第二個(gè)參數(shù)是一個(gè)數(shù)組依賴,如果數(shù)組里面的值有變化,那么就會(huì)重新去執(zhí)行第一個(gè)參數(shù)里面的函數(shù),并將函數(shù)返回的值緩存起來并作為 useMemo 的返回值 。

注意

  • 如果沒有提供依賴項(xiàng)數(shù)組,useMemo 在每次渲染時(shí)都會(huì)計(jì)算新的值;
  • 計(jì)算量如果很小的計(jì)算函數(shù),也可以選擇不使用 useMemo,因?yàn)檫@點(diǎn)優(yōu)化并不會(huì)作為性能瓶頸的要點(diǎn),反而可能使用錯(cuò)誤還會(huì)引起一些性能問題。

遍歷展示視圖時(shí)使用key

key 幫助 React 識(shí)別哪些元素改變了,比如被添加或刪除。因此你應(yīng)當(dāng)給數(shù)組中的每一個(gè)元素賦予一個(gè)確定的標(biāo)識(shí)。

  1. const numbers = [12345]; 
  2. const listItems = numbers.map((number) => 
  3.   <li key={number.toString()}> 
  4.     {number} 
  5.   </li> 
  6. ); 

使用key注意事項(xiàng):

  • 最好是這個(gè)元素在列表中擁有的一個(gè)獨(dú)一無二的字符串。通常,我們使用數(shù)據(jù)中的 id 來作為元素的 key,當(dāng)元素沒有確定 id 的時(shí)候,萬不得已你可以使用元素索引 index 作為 key
  • 元素的 key 只有放在就近的數(shù)組上下文中才有意義。例如,如果你提取出一個(gè) ListItem 組件,你應(yīng)該把 key 保留在數(shù)組中的這個(gè) 元素上,而不是放在 ListItem 組件中的
  • 元素上。

合理設(shè)計(jì)組件

簡(jiǎn)化props

如果一個(gè)組件的props比較復(fù)雜的話,會(huì)影響shallowCompare的效率,也會(huì)使這個(gè)組件變得難以維護(hù),另外也與“單一職責(zé)”的原則不符合,可以考慮進(jìn)行拆解。

簡(jiǎn)化State

在設(shè)計(jì)組件的state時(shí),可以按照這個(gè)原則來:需要組件響應(yīng)它的變動(dòng)或者需要渲染到視圖中的數(shù)據(jù),才放到 state 中;這樣可以避免不必要的數(shù)據(jù)變動(dòng)導(dǎo)致組件重新渲染。

責(zé)任編輯:張燕妮 來源: lsqy'blog
相關(guān)推薦

2019-02-25 07:07:38

技巧React 優(yōu)化

2016-12-19 10:00:00

React性能優(yōu)化

2022-08-03 09:11:31

React性能優(yōu)化

2023-11-01 17:57:56

React應(yīng)用程序性能

2022-05-23 13:44:53

前端開發(fā)優(yōu)化

2012-09-11 15:43:32

HBase

2020-06-22 07:30:00

React開發(fā)工具

2019-03-14 15:38:19

ReactJavascript前端

2009-12-24 16:46:03

WPF性能優(yōu)化

2020-12-20 10:02:17

ContextReactrender

2022-09-13 12:56:28

前端優(yōu)化

2017-12-04 12:29:15

前端JavaScript性能優(yōu)化

2015-11-05 09:02:05

Java代碼性能優(yōu)化

2015-07-09 13:19:17

Ceph分布式存儲(chǔ)性能調(diào)優(yōu)

2021-06-17 08:59:45

React前端優(yōu)化

2022-03-11 10:23:02

React性能優(yōu)化

2021-11-05 10:36:19

性能優(yōu)化實(shí)踐

2010-12-10 10:17:21

關(guān)系型數(shù)據(jù)庫

2022-05-27 16:03:37

ReactDanVue

2018-11-20 10:50:00

Java性能優(yōu)化編程技巧
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)