最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React Hooks之使用useCallback和useMemo進(jìn)行性能優(yōu)化方式

 更新時(shí)間:2023年06月06日 16:53:54   作者:學(xué)全棧的灌湯包  
這篇文章主要介紹了React Hooks之使用useCallback和useMemo進(jìn)行性能優(yōu)化方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

useCallback和useMemo性能優(yōu)化

useCallback的解析

useCallback的使用

useCallback實(shí)際的目的是為了進(jìn)行性能的優(yōu)化。

useCallback進(jìn)行什么樣的優(yōu)化呢?

例如下面這個(gè)計(jì)數(shù)器的案例, 我們點(diǎn)擊按鈕時(shí), counter數(shù)據(jù)會發(fā)生變化, App函數(shù)組件就會重新渲染, 意味著increment函數(shù)就會被重新定義一次, 每點(diǎn)擊一次按鈕, increment函數(shù)就會重新被定義;

雖然每次定義increment函數(shù), 垃圾回收機(jī)制會將上一次定義的increment函數(shù)回收, 但是這種不必要的重復(fù)定義是會影響性能的

import React, { memo, useState } from 'react'
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? function increment() {
? ? setCounter(counter + 1)
? }
? return (
? ? <div>
? ? ? <h2>{counter}</h2>
? ? ? <button onClick={() => increment()}>+1</button>
? ? </div>
? )
})
export default App

如何進(jìn)行性能的優(yōu)化呢?

調(diào)用useCallback會返回一個(gè) memoized(有記憶的) 的回調(diào)函數(shù);

在依賴不變的情況下,多次定義的時(shí)候,返回的回調(diào)函數(shù)是相同的;

  • 參數(shù)一: 傳入一個(gè)回調(diào)函數(shù), 如果依賴發(fā)生改變會定義一個(gè)新的該回調(diào)函數(shù)使用, 如果依賴沒有發(fā)生改變, 依然使用原來的回調(diào)函數(shù)
  • 參數(shù)二: 用于控制依賴的, 第二個(gè)參數(shù)要求傳入一個(gè)數(shù)組, 數(shù)組中可以傳入依賴, 傳空數(shù)組表示沒有依賴
const memoizedCallback = useCallback(
? () => {
? ?doSomething(a, b)
? },
? [a, b]
)

useCallback拿到的結(jié)果是函數(shù)

useCallback的作用

通常使用useCallback的目的是在向子組件傳遞函數(shù)時(shí), 將要傳遞的函數(shù)進(jìn)行優(yōu)化在傳遞給子組件, 避免子組件進(jìn)行多次渲染;

并不是為了函數(shù)不再重新定義, 也不是對函數(shù)定義做優(yōu)化

我們來看下面這樣一個(gè)案例:

定義一個(gè)子組件Test, 并將increment函數(shù)傳遞到子組件中, 我們在子組件中可以拿到increment方法修改App組件中的counter;

由于counter發(fā)生改變, 就會重新定義一個(gè)新的increment函數(shù), 因此我們只要修改了counter, 就會傳遞一個(gè)新的increment函數(shù)到Test組件中; Test組件中的props就會發(fā)生變化, Test組件會被重新渲染

import React, { memo, useState, useCallback } from 'react'
const Test = memo((props) => {
? console.log("Test組件被重新渲染")
? return (
? ? <div>
? ? ? <button onClick={props.increment}>Test+1</button>
? ? </div>
? )
})
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? function increment() {
? ? setCounter(counter + 1)
? }
? return (
? ? <div>
? ? ? <h2>{counter}</h2>
? ? ? <button onClick={increment}>+1</button>
? ? ? <Test increment={increment}/>
? ? </div>
? )
})
export default App

如果此時(shí)App組件中再定義一個(gè)方法changeMessage用來修改message;

我們會發(fā)現(xiàn)當(dāng)message發(fā)生改變時(shí), 子組件Test也會被重新渲染; 這是因?yàn)閙essage發(fā)生改變, App組件會重新渲染, 那么就會重新定義一個(gè)新的increment函數(shù), 將新的increment函數(shù)傳遞到Test組件, Test組件的props發(fā)生改變就會重新渲染

import React, { memo, useState, useCallback } from 'react'
const Test = memo((props) => {
? console.log("Test組件被重新渲染")
? return (
? ? <div>
? ? ? <button onClick={props.increment}>Test+1</button>
? ? </div>
? )
})
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? const [message, setMessage] = useState("哈哈哈哈")
? function increment() {
? ? setCounter(counter + 1)
? }
? return (
? ? <div>
? ? ? <h2>{counter}</h2>
? ? ? <button onClick={increment}>+1</button>
? ? ? <h2>{message}</h2>
? ? ? <button onClick={() => setMessage("呵呵呵呵")}>修改message</button>
? ? ? <Test increment={increment}/>
? ? </div>
? )
})
export default App

但是如果我們使用useCallback, 就可以避免App組件中message發(fā)生改變時(shí), Test組件重新渲染

因?yàn)閙essage組件發(fā)生改變, 但是我們下面的useCallback函數(shù)是依賴counter的, 在依賴沒有發(fā)生改變時(shí), 多次定義返回的值是相同的(也就是修改message重新渲染App組件時(shí), increment并沒有重新定義, 依然是之前的); 就意味著Test組件中的props沒有改變, 因此Test組件不會被重新渲染

如果是counter值發(fā)生改變, 因?yàn)閡seCallback函數(shù)是依賴counter的, 所以會定義一個(gè)新的函數(shù)給increment; 當(dāng)向Test組件傳遞新的increment時(shí), Test組件的props就會改變, Test依然會重新渲染, 這也是我們想要實(shí)現(xiàn)的效果

import React, { memo, useState, useCallback } from 'react'
const Test = memo((props) => {
? console.log("Test組件被重新渲染")
? return (
? ? <div>
? ? ? <button onClick={props.increment}>Test+1</button>
? ? </div>
? )
})
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? const [message, setMessage] = useState("哈哈哈哈")
? // 使用useCallback依賴于counter
? const increment = useCallback(() => {
? ? setCounter(counter + 1)
? }, [counter])
? return (
? ? <div>
? ? ? <h2>{counter}</h2>
? ? ? <button onClick={increment}>+1</button>
? ? ? <h2>{message}</h2>
? ? ? <button onClick={() => setMessage("呵呵呵呵")}>修改message</button>
? ? ? <Test increment={increment}/>
? ? </div>
? )
})
export default App

還可以再進(jìn)一步的進(jìn)行優(yōu)化:

現(xiàn)在我們的代碼是counter發(fā)生變化時(shí), useCallback會重新定義一個(gè)新的函數(shù)返回給increment; 但是我們想做到, counter發(fā)生變化, 依然使用原來的函數(shù), 不需要重新定義一個(gè)新的函數(shù);

可能會有小伙伴想, 直接將依賴改為一個(gè)空數(shù)組, 但是如果是這樣的話就會產(chǎn)生閉包陷阱;

我們修改counter時(shí)確實(shí)不會重新生成一個(gè)新的函數(shù), 但是原來的函數(shù)中使用的counter永遠(yuǎn)是之前的值, 也就是0;

這是因?yàn)槲覀兣f的函數(shù)在定義的那一刻, counter的值是0;

由于修改counter依然使用舊的函數(shù), 這樣無論我們修改多少次counter, 頁面展示的數(shù)據(jù)永遠(yuǎn)是 0 + 1 的結(jié)果

const increment = useCallback(() => {
? setCounter(counter + 1)
}, [])

這個(gè)時(shí)候我們就需要結(jié)合使用另一個(gè)hook: useRef

useRef函數(shù)在組件多次進(jìn)行渲染時(shí), 返回的是同一個(gè)值;

我們就可以將最新的counter儲存到useRef返回的對象的current屬性中;

這樣做的好處就是, counter發(fā)生改變時(shí), 也不會重新定義一個(gè)函數(shù), 意味著修改counter也不會導(dǎo)致Test組件重新渲染

import React, { memo, useState, useCallback, useRef } from 'react'
const Test = memo((props) => {
? console.log("Test組件被重新渲染")
? return (
? ? <div>
? ? ? <button onClick={props.increment}>Test+1</button>
? ? </div>
? )
})
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? const [message, setMessage] = useState("哈哈哈哈")
? // 組件進(jìn)行多次渲染, 返回的是同一個(gè)ref對象
? const counterRef = useRef()
? // 將最新的counter保存到ref對象current屬性中
? counterRef.current = counter
? const increment = useCallback(() => {
? ? // 在修改數(shù)據(jù)時(shí), 引用保存到ref對象current屬性的最新的值
? ? setCounter(counterRef.current + 1)
? }, [])
? return (
? ? <div>
? ? ? <h2>{counter}</h2>
? ? ? <button onClick={increment}>+1</button>
? ? ? <Test increment={increment}/>
? ? ? <h2>{message}</h2>
? ? ? <button onClick={() => setMessage("呵呵呵呵")}>修改message</button>
? ? </div>
? )
})
export default App

useMemo的解析

useMemo實(shí)際的目的也是為了進(jìn)行性能的優(yōu)化, 例如下面這個(gè)例子

我們定義一個(gè)計(jì)算累加的函數(shù)calcNumTotal, 在App組件中調(diào)用這個(gè)函數(shù)計(jì)算結(jié)果

但是counter改變時(shí), App組件就會重新渲染, 那么calcNumTotal函數(shù)又會重新計(jì)算; 但是counter的改變和calcNumTotal函數(shù)并沒有關(guān)系, 卻要重新渲染; 這種類似的場景我們就可以使用useMemo進(jìn)行性能優(yōu)化

import React, { memo } from 'react'
import { useState } from 'react'
// 定義一個(gè)函數(shù)求和
function calcNumTotal(num) {
? let total = 0
? for (let i = 1; i <= num; i++) {
? ? total += i
? }
? return total
}
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? return (
? ? <div>
? ? ? {/* couter改變, 組件重新渲染, 意味著calcNumTotal函數(shù)也會重新執(zhí)行, 重新計(jì)算結(jié)果 */}
? ? ? <h2>計(jì)算結(jié)果: {calcNumTotal(100)}</h2>
? ? ? <h2>當(dāng)前計(jì)數(shù): {counter}</h2>
? ? ? <button onClick={() => setCounter(counter + 1)}>+1</button>
? ? </div>
? )
})
export default App

如何使用 useMemo進(jìn)行性能的優(yōu)化呢?

useMemo返回的也是一個(gè) memoized(有記憶的) 值; 在依賴不變的情況下,多次定義的時(shí)候,返回的值是相同的;

  • 參數(shù)一: 傳入一個(gè)回調(diào)函數(shù)
  • 參數(shù)二: 傳入一個(gè)數(shù)組, 表示依賴, 什么都不依賴傳入空數(shù)組; 如果不傳則該函數(shù)什么都不會做, 無意義
const memoizedValue = useMemo(
? () => {
? ? computeExpensiveValue(a, b)
? },?
? [a, b]
)

這樣我們就可以對上面的代碼進(jìn)行優(yōu)化了, 實(shí)現(xiàn)counter發(fā)生變化, 而calcNumTotal函數(shù)不需要重新計(jì)算結(jié)果

import React, { memo, useMemo, useState } from 'react'
// 定義一個(gè)函數(shù)求和
function calcNumTotal(num) {
? console.log("calcNumTotal函數(shù)被調(diào)用")
? let total = 0
? for (let i = 1; i <= num; i++) {
? ? total += i
? }
? return total
}
const App = memo(() => {
? const [counter, setCounter] = useState(10)
? let result = useMemo(() => {
? ? return calcNumTotal(50)
? }, [])
? return (
? ? <div>
? ? ? {/* couter改變, 組件重新渲染, 意味著calcNumTotal函數(shù)也會重新執(zhí)行, 重新計(jì)算結(jié)果 */}
? ? ? <h2>計(jì)算結(jié)果: {result}</h2>
? ? ? <h2>當(dāng)前計(jì)數(shù): {counter}</h2>
? ? ? <button onClick={() => setCounter(counter + 1)}>+1</button>
? ? </div>
? )
})
export default App

useMemo與useCallback的區(qū)別:

useMemo拿到的傳入回調(diào)函數(shù)的返回值, useCallback拿到的傳入的回調(diào)函數(shù)本身;

簡單來說useMemo是對函數(shù)的返回值做優(yōu)化, useCallback是對函數(shù)做優(yōu)化;

useCallback(fn, [])和uesMemo(() => fn, [])表達(dá)的是同一個(gè)意思

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 如何使用Next.js?+?Prisma?+?MySQL開發(fā)全棧項(xiàng)目

    如何使用Next.js?+?Prisma?+?MySQL開發(fā)全棧項(xiàng)目

    在Next.js項(xiàng)目的任何地方都可以使用Prisma來訪問數(shù)據(jù)庫,Prisma提供了豐富的查詢方法,滿足您在項(xiàng)目中的各種需求,這篇文章主要介紹了如何使用Next.js+Prisma+MySQL開發(fā)全棧項(xiàng)目的相關(guān)資料,需要的朋友可以參考下
    2026-03-03
  • react源碼中的生命周期和事件系統(tǒng)實(shí)例解析

    react源碼中的生命周期和事件系統(tǒng)實(shí)例解析

    這篇文章主要為大家介紹了react源碼中的生命周期和事件系統(tǒng)實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • DOM 注入實(shí)踐之如何在 React 中優(yōu)雅地?cái)U(kuò)展第三方組件

    DOM 注入實(shí)踐之如何在 React 中優(yōu)雅地?cái)U(kuò)展第三方組件

    本文將介紹一種更優(yōu)雅的解決方案——DOM 注入 + React Portal,通過抽象的示例展示這種技術(shù)方案的應(yīng)用場景和實(shí)現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • react開發(fā)中如何使用require.ensure加載es6風(fēng)格的組件

    react開發(fā)中如何使用require.ensure加載es6風(fēng)格的組件

    本篇文章主要介紹了react開發(fā)中如何使用require.ensure加載es6風(fēng)格的組件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • React創(chuàng)建虛擬DOM的兩種方式小結(jié)

    React創(chuàng)建虛擬DOM的兩種方式小結(jié)

    本文主要介紹了兩種創(chuàng)建React虛擬DOM的方式,包括JS方式和jsx方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • ReactNative列表ListView的用法

    ReactNative列表ListView的用法

    本篇文章主要介紹了ReactNative列表ListView的用法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 使用React?MUI庫實(shí)現(xiàn)用戶列表分頁功能

    使用React?MUI庫實(shí)現(xiàn)用戶列表分頁功能

    MUI是一款基于React的UI組件庫,可以方便地構(gòu)建美觀的用戶界面,使用MUI的DataTable組件和分頁器組件可以輕松實(shí)現(xiàn)用戶列表分頁功能,這篇文章使用MUI庫實(shí)現(xiàn)了用戶列表分頁功能,感興趣的同學(xué)可以參考下文
    2023-05-05
  • React Hook 監(jiān)聽localStorage更新問題

    React Hook 監(jiān)聽localStorage更新問題

    這篇文章主要介紹了React Hook 監(jiān)聽localStorage更新問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • React中的路由嵌套和手動實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解

    React中的路由嵌套和手動實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解

    這篇文章主要介紹了React中的路由嵌套和手動實(shí)現(xiàn)路由跳轉(zhuǎn)的方式,手動路由的跳轉(zhuǎn),主要是通過Link或者NavLink進(jìn)行跳轉(zhuǎn)的,實(shí)際上我們也可以通JavaScript代碼進(jìn)行跳轉(zhuǎn),需要的朋友可以參考下
    2022-11-11
  • 聊聊ant?design?charts?獲取后端接口數(shù)據(jù)展示問題

    聊聊ant?design?charts?獲取后端接口數(shù)據(jù)展示問題

    今天在做項(xiàng)目的時(shí)候遇到幾個(gè)讓我很頭疼的問題,一個(gè)是通過后端接口成功訪問并又返回?cái)?shù)據(jù),但拿不到數(shù)據(jù)值。其二是直接修改state中的data,console中數(shù)組發(fā)生變化但任然數(shù)據(jù)未顯示,這篇文章主要介紹了ant?design?charts?獲取后端接口數(shù)據(jù)展示,需要的朋友可以參考下
    2022-05-05

最新評論

秭归县| 大竹县| 汝州市| 彰武县| 确山县| 六安市| 嵊泗县| 楚雄市| 福泉市| 禄丰县| 仁化县| 扎囊县| 社旗县| 兴业县| 察雅县| 平阴县| 襄汾县| 徐汇区| 温宿县| 河西区| 嘉峪关市| 温州市| 兰州市| 永城市| 周至县| 贵州省| 左云县| 蒙阴县| 云和县| 安国市| 洞头县| 江门市| 福贡县| 康乐县| 宾阳县| 饶河县| 获嘉县| 定安县| 观塘区| 江都市| 武鸣县|