成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何做 React 性能優(yōu)化?

開(kāi)發(fā) 前端
今天帶大家來(lái)學(xué)習(xí)如何做 React 性能優(yōu)化。

大家好,我是前端西瓜哥。今天帶大家來(lái)學(xué)習(xí)如何做 React 性能優(yōu)化。

使用 React.memo()

一個(gè)組件可以通過(guò) React.memo 方法得到一個(gè)添加了緩存功能的新組件。

const Comp = props => {  //}const MemorizedComp = React.memo(Comp);

再次渲染時(shí),如果 props 沒(méi)有發(fā)生改變,就跳過(guò)該組件的重渲染,以實(shí)現(xiàn)性能優(yōu)化。

這里的 關(guān)鍵在于 props 不能改變,這也是最?lèi)盒牡牡胤健?/p>

對(duì)于像是字符串、數(shù)值這些基本類(lèi)型,對(duì)比沒(méi)有問(wèn)題。但對(duì)于對(duì)象類(lèi)型,就要做一些緩存工作,讓原本沒(méi)有改變的對(duì)象或函數(shù)仍舊指向同一個(gè)內(nèi)存對(duì)象。

因?yàn)槊看魏瘮?shù)組件被執(zhí)行時(shí),里面聲明的函數(shù)都是一個(gè)全新的函數(shù),和原來(lái)的函數(shù)指向不同的內(nèi)存空間,全等比較結(jié)果是 false。

處理 props 比較問(wèn)題

React.memo() 最疼痛的就是處理 props 比較問(wèn)題。

我們看個(gè)例子:

const MemorizedSon = React.memo(({ onClick }) => {  // ...})const Parent() {  // ...  const onClick = useCallback(() => {    // 一些復(fù)雜的判斷和邏輯  }, [a, setA, b, onSava]);  return (    <div>      <MemorizedSon onClick={onClick} />    </div>  )}

上面為了讓函數(shù)的引用不變,使用了 useCallback。函數(shù)里用到了一些變量,因?yàn)楹瘮?shù)組件有閉包陷阱,可能會(huì)導(dǎo)致指向舊狀態(tài)問(wèn)題,所以需要判斷這些變量是否變化,來(lái)決定是否使用緩存函數(shù)。

這里就出現(xiàn)了一個(gè) 連鎖反應(yīng),就是我還要給變量中的對(duì)象類(lèi)型做緩存,比如這里的 setA 和 onSave 函數(shù)。然后這些函數(shù)可以又依賴(lài)其他函數(shù),一直連鎖下去,然后你發(fā)現(xiàn)有些函數(shù)甚至來(lái)自其他組件,通過(guò) props 注入。

啊我真的是麻了呀,我優(yōu)雅的 React 一下變得丑陋不堪。

怎么辦,一個(gè)方式是用 ref。ref 沒(méi)有閉包問(wèn)題,且能夠在組件每次更新后保持原來(lái)的指向。

const MemorizedSon = React.memo(({ onClickRef }) => {  const onClick = onClickRef.current;})const Parent() {  // ...  const onClick = () => {    // 一些復(fù)雜的判斷和邏輯  };    const onClickRef = useRef(onClick);  onClickRef.current = onClick;    return (    <div>      <MemorizedSon onClickRef={onClickRef} />    </div>  )}

或者

const MemorizedSon = React.memo(({ onClick }) => {  // ...})const Parent() {  // ...  const onClick = useCallback(() => {    const {a, b} = propsRef.current;    const {setA, setSave} = stateRef.current;    // 一些復(fù)雜的判斷和邏輯  }, []);  return (    <div>      <MemorizedSon onClick={onClick} />    </div>  )}

當(dāng)然官方也注意到這種場(chǎng)景,提出了 useEvent 的提案,希望能盡快實(shí)裝吧。

function Chat() {  const [text, setText] = useState('');  const onClick = useEvent(() => {    sendMessage(text);  });  return <SendButton onClick={onClick} />;}

The code inside useEvent “sees” the props/state values at the time of the call. The returned function has a stable identity even if the props/state it references change. There is no dependency array。

用了 useEvent 后,指向是穩(wěn)定的,不需要加依賴(lài)項(xiàng)數(shù)組。

提案詳情具體看下面這個(gè)鏈接:

https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md。

跳過(guò)中間組件

假設(shè)我們的組件嵌套是這樣的:A -> B -> C。

其中 C 需要拿到 A 的一個(gè)狀態(tài)。B 雖然不需要用到 A 的任何狀態(tài),但為了讓 C 拿到狀態(tài),所以也用 props 接收了這個(gè),然后再傳給 C。

這樣的話,A 更新?tīng)顟B(tài)時(shí),B 也要進(jìn)行不必要的重渲染。

對(duì)于這種情況,我們可以讓中間組件 B 跳過(guò)渲染:

  1. 給 B 應(yīng)用 React.memo,A 的狀態(tài)不再傳給 B。
  2. A 的狀態(tài)通過(guò)發(fā)布訂閱的方式傳給 C(比如 useContext,或通過(guò)狀態(tài)管理)。

狀態(tài)下放

假設(shè)同樣還是 A -> B -> C 形式的組件嵌套。

C 需要來(lái)自 A 的狀態(tài),B 會(huì)幫忙通過(guò) props 傳遞狀態(tài)過(guò)來(lái)。A 狀態(tài)更新時(shí),A、B、C 都會(huì)重渲染。

如果狀態(tài)只有 C 一個(gè)組件會(huì)用到,我們可以考慮直接把狀態(tài)下放到 C。這樣當(dāng)狀態(tài)更新時(shí),就只會(huì)渲染 C。

組件提升

將組件提升到父組件的 props 上。

export default function App() {  return (    <ColorPicker>      <p>Hello, world!</p>      <ExpensiveTree />    </ColorPicker>  );}function ColorPicker({ children }) {  let [color, setColor] = useState("red");  return (    <div style={{ color }}>      <input value={color} onChange={(e) => setColor(e.target.value)} />      {children}    </div>  );}

在這里 ColorPicker 更新 color 狀態(tài)后,因?yàn)?ExpensiveTree 來(lái)自外部 props,不會(huì)改變,不會(huì)重渲染。除非是 App 中發(fā)生了狀態(tài)改變。

正確使用列表 key

進(jìn)行列表渲染時(shí),React 會(huì)要求你給它們提供 key,讓 React 識(shí)別更新后的位置變化,避免一些不必要的組件樹(shù)銷(xiāo)毀和重建工作。

比如你的第一個(gè)元素是 div,更新后發(fā)生了位置變化,第一個(gè)變成了 p。如果你不通過(guò) key 告知新位置,React 就會(huì)將 div 下的整棵樹(shù)銷(xiāo)毀,然后構(gòu)建 p 下的整棵樹(shù),非常耗費(fèi)性能。

如果你提供了位置,React 就會(huì)做真實(shí) DOM 的位置移動(dòng),然后做樹(shù)的更新,而不是銷(xiāo)毀和重建。

注意狀態(tài)管理庫(kù)的觸發(fā)更新機(jī)制

對(duì)于使用 Redux 的進(jìn)行狀態(tài)管理的朋友來(lái)說(shuō),我們會(huì)在函數(shù)組件中通過(guò) useSelector 來(lái)訂閱狀態(tài)的變化,自動(dòng)更新組件。

const Comp() {  const count = useSelector(state => state.count);}

useSelector 做了什么事?它會(huì)訂閱 state 的變化,當(dāng) state 變化時(shí),會(huì)運(yùn)行回調(diào)函數(shù)得到返回值,和上一次的返回值進(jìn)行全等比較。如果相等,不更新組件;如果不等,更新組件。然后緩存這次的返回值。

上面這種情況還好,我們?cè)倏纯磳?xiě)成對(duì)象的形式。

import { shallowEqual, useSelector } from 'react-redux'const Comp() {  const { count, username } = useSelector(state => ({    count: state.count,    username: state.username  }), shallowEqual);}

上面這種寫(xiě)法,因?yàn)槟J(rèn)用的是全等比較,所以每次 state 更新后比較結(jié)果都是 false,組件每次都更新。對(duì)于組合成的對(duì)象,你要用 shallowEqual 淺比較來(lái)替代全等比較,避免不必要的更新。

有一種情況比較特別,假設(shè) state.userInfo 有多個(gè)屬性,username、age、acount、score、level 等。有些人會(huì)這樣寫(xiě):

const Comp() {  const { username, age } = useSelector(state => state.userInfo), shallowEqual);}

看起來(lái)沒(méi)什么問(wèn)題,但里面是有陷阱的:雖然我們的組件只用到 username 和 age,但 useSelector 卻會(huì)對(duì)整個(gè) userInfo 對(duì)象做比較。

假設(shè)我們只更新了 userInfo.level,useSelector 的比較結(jié)果就為 false 了,導(dǎo)致組件更新,即使你沒(méi)有用上 level,這不是我們期望的。

所以正確的寫(xiě)法應(yīng)該是:

const Comp() {  const { username, age } = useSelector(state => {    const { username, age } = state.userInfo;    return { username, age };  }), shallowEqual);}

使用 useSelector 監(jiān)聽(tīng)狀態(tài)變化,一定要關(guān)注 state 的粒度問(wèn)題。

Context 是粗粒度的

React 提供的 Context 的粒度是粗粒度的。

當(dāng) Context 的值變化時(shí),用到該 Context 的組件就會(huì)更新。

有個(gè)問(wèn)題,就是 我們提供的 Context 值通常都是一個(gè)對(duì)象,比如:

const App = () => {  return (    <EditorContext.Provider value={ visible, setVisible }>      <Editor />    </EditorContext.Provider>  );}

每當(dāng) Context 的 value 變化時(shí),用到這個(gè) Context 的組件都會(huì)被更新,即使你只是用這個(gè) value 的其中一個(gè)屬性,且它沒(méi)有改變。

因?yàn)?nbsp;Context 是粗粒度的。

所以你或許可以考慮在高一些層級(jí)的組件去獲取 Context,然后通過(guò) props 分別注入到用到 Context 的不同部分的組件中。

順便一提,Context 的 value 在必要時(shí)也要做緩存,以防止組件的無(wú)意義更新。

const App = () => {  const EditorContextVal = useMemo(() => ({ visible, setVisible }), [visible, setVisible]);  return (    <EditorContext.Provider value={ visible, setVisible }>      <Editor />    </EditorContext.Provider>  );}

批量更新

有一個(gè)經(jīng)典的問(wèn)題是:React 的 setState 是同步還是異步的?

答案是副作用或合成事件響應(yīng)函數(shù)內(nèi),是異步的,會(huì)批量執(zhí)行。其他情況(比如 setTimeout)則會(huì)同步執(zhí)行,同步的問(wèn)題是會(huì)立即進(jìn)行組件更新渲染,一次有多個(gè)同步 setState 就可能會(huì)有性能問(wèn)題。

我們可以用ReactDOM.unstable_batchedUpdates 來(lái)將本來(lái)需要同步執(zhí)行的狀態(tài)更新變成批量的。

ReactDOM.unstable_batchedUpdates(() => {  setScore(score + 1);  setUserName('前端西瓜哥');})

不過(guò)到了 React18 后,開(kāi)啟并發(fā)模式的話,就沒(méi)有同步問(wèn)題了,所有的 setState 都是異步的。

Redux 的話,你可以考慮使用批量更新插件:redux-batched-actions。

import { batchActions } from 'redux-batched-actions';dispatch(batchActions([  setScoreAction(score + 1),  setUserName('前端西瓜哥')]));

redux-batched-actions 中間件確實(shí)會(huì)將多個(gè) actions 做一個(gè)打包組合再 dispatch,你會(huì)發(fā)現(xiàn) store.subscribe 的回調(diào)函數(shù)觸發(fā)次數(shù)確實(shí)變少了。

但如果你用了 react-redux 庫(kù)的話,這個(gè)庫(kù)其實(shí)在多數(shù)情況下并沒(méi)有什么用。

因?yàn)?react-redux 其實(shí)已經(jīng)幫我們做了批量處理操作,同步的多個(gè) dispatch 執(zhí)行完后,才會(huì)通知組件進(jìn)行重渲染。

懶加載

有些組件,如果可以的話,可以讓組件直接不渲染,做一個(gè)懶加載。比如:

{visible && <Model />}

結(jié)尾

React 的優(yōu)化門(mén)道還是挺多的,其中的 React.memo 優(yōu)化起來(lái)確實(shí)復(fù)雜,一不小心還會(huì)整成負(fù)優(yōu)化。

所以,不要 過(guò)早進(jìn)行優(yōu)化。

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2025-03-31 01:55:00

2011-03-01 10:42:23

無(wú)線局域網(wǎng)局域網(wǎng)性能優(yōu)化

2012-05-07 08:49:57

Clojure

2023-12-29 08:29:15

QPS系統(tǒng)應(yīng)用

2020-02-05 14:49:04

網(wǎng)絡(luò)性能優(yōu)化微調(diào)

2021-08-27 14:26:06

開(kāi)發(fā)技能React

2022-07-25 08:02:57

Tomcat調(diào)優(yōu)組件

2022-12-07 11:21:30

Reactdiff

2019-02-25 07:07:38

技巧React 優(yōu)化

2016-12-19 10:00:00

React性能優(yōu)化

2023-11-01 17:57:56

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

2022-08-29 08:08:58

SQLOracleCPU

2021-07-30 05:05:32

場(chǎng)景

2020-06-22 07:30:00

React開(kāi)發(fā)工具

2015-07-30 11:21:16

代碼審查

2020-12-18 10:40:00

ExcelJava代碼

2019-03-14 15:38:19

ReactJavascript前端

2012-03-12 16:42:54

測(cè)試

2022-02-17 13:18:58

定價(jià)模型營(yíng)銷(xiāo)AHP
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 免费美女网站 | 免费精品一区 | 亚洲日韩中文字幕一区 | 亚洲精品视频免费观看 | 中文字幕在线免费观看 | 国产精品久久久久久久久久免费 | 国产精品日韩欧美一区二区三区 | 国产一区二区麻豆 | 欧美综合自拍 | 香蕉视频在线播放 | 精品久久香蕉国产线看观看亚洲 | 亚洲一区二区三区福利 | 亚洲成人自拍 | 在线第一页 | 涩涩视频在线观看 | 日韩中文久久 | 97久久精品午夜一区二区 | 国产四区 | 中文字幕在线看第二 | 欧美a免费 | 色噜噜色综合 | 午夜电影日韩 | 日本久久精品 | 欧美精品一区二区在线观看 | 免费观看a级毛片在线播放 黄网站免费入口 | 成人一区二区三区 | 欧美性tv | 成人在线视频看看 | 91操操操| 精品久久99| 久久久久国产精品 | 中文字幕视频在线观看免费 | 国产色在线 | 欧美激情综合五月色丁香小说 | 国产欧美日韩综合精品一区二区 | 韩日一区二区 | 精品1区2区 | 免费毛片网站 | 久久99精品久久久久久国产越南 | 欧美日韩精品影院 | ww亚洲ww亚在线观看 |