日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
從源碼理清UseEffect第二個(gè)參數(shù)是怎么處理的

useEffect 是常用的 hook,它支持兩個(gè)參數(shù),第一個(gè)參數(shù)是回調(diào)函數(shù),第二個(gè)參數(shù)是依賴。

當(dāng)?shù)诙€(gè)參數(shù)為 null 或 undefined 的時(shí)候,回調(diào)函數(shù)每次 render 都會(huì)執(zhí)行,而參數(shù)為數(shù)組的時(shí)候,只有依賴項(xiàng)變了才會(huì)執(zhí)行。

這些我們都很熟悉了,但它是怎么實(shí)現(xiàn)的呢?我們來從源碼來找下答案。

useEffect 的第二個(gè)參數(shù)

我們先來試一下第二個(gè)參數(shù)傳入 undefined、空數(shù)組、有依賴的數(shù)組時(shí)的效果。

準(zhǔn)備這樣一段代碼:

import { useEffect, useRef, useState } from 'react';

function Dong() {
const ref = useRef(1);
const [,setState] = useState();
useEffect(() => {
console.log(111);
});
useEffect(() => {
console.log(222);
}, []);
useEffect(() => {
console.log(333);
}, [ref.current]);
useEffect(() => {
setInterval(() => {
setState([]);
}, 1000);
setTimeout(() => {
ref.current = 2;
}, 3000);
}, []);
return
dong
;
}

我們用寫了三個(gè) useEffect,第二個(gè)參數(shù)分別為 undefined、[]、有一個(gè)依賴的數(shù)組,回調(diào)函數(shù)里分別打印 111、222、333。

然后 useState 聲明了一個(gè) state,用 setInterval 定時(shí)修改,這樣能不斷觸發(fā) render。

又用 useRef 聲明了一個(gè)對(duì)象,它的特點(diǎn)是每次 render 都是返回的同一個(gè)對(duì)象,我們用 setTimeout 在 2s 后修改了它的值。

執(zhí)行的結(jié)果大家應(yīng)該很容易想到:

111 每次都會(huì)打印,因?yàn)榈诙€(gè)參數(shù)為 undefined。

222 只打印一次,因?yàn)榈诙€(gè)參數(shù)為 []。

333 打印兩次,因?yàn)榈诙€(gè)參數(shù)有一個(gè)依賴,這個(gè)依賴在 2s 的時(shí)候會(huì)變一次。

這些我們都很熟悉了,但是它為什么是這樣呢?

我們來看下源碼:

useEffect 相關(guān)源碼

react hooks 的原理前面一篇文章寫過,我們?cè)龠^一遍:

jsx 編譯產(chǎn)生 render function,執(zhí)行返回 vdom,但是為了提高性能,React 16 引入 fiber 架構(gòu),會(huì)先把 vdom 轉(zhuǎn)成 fiber,然后再去更新到 dom。

vdom 轉(zhuǎn) fiber 的過程叫做 reconcile,更新到 dom 的過程叫做 commit。reconcile 的過程是可打斷的,需要 schedule。

hooks 也是基于 fiber 來實(shí)現(xiàn)的,它在 fiber 節(jié)點(diǎn)上維護(hù)了一個(gè)鏈表(memorizedState 屬性),用來保存數(shù)據(jù),每個(gè) hook 都是從對(duì)應(yīng)的鏈表元素上存取各自的數(shù)據(jù)。

比如上面那個(gè)組件的 6 個(gè) hook 就對(duì)應(yīng)著 fiber 節(jié)點(diǎn)上 memorizedState 鏈表的 6 個(gè)元素:

每個(gè) hook 都是在對(duì)應(yīng)的鏈表元素上存取數(shù)據(jù)的。

這個(gè)鏈表有個(gè)建立的過程,叫做 mount,后面只需要 update,所以每個(gè) hook 的實(shí)現(xiàn)都會(huì)分為 mount 和 update 兩個(gè)階段。

我們看下 useEffect 相關(guān)的源碼:

它也是分為了 mountEffect 和 updateEffect 兩個(gè)函數(shù),最終都是在 hook.memorizedState 存取元素的。這就是 hook 的通用原理。

第二個(gè)參數(shù)對(duì)應(yīng)的就是 deps,它是怎么判斷是否要更新的呢?

我們著重看下這段邏輯:

deps 是新傳入的參數(shù),如果是 undefined 會(huì)作為 null。

hook.memorizedState.deps 取到的是之前的 deps。

然后新舊 deps 會(huì)做下對(duì)比,如果返回 true 才會(huì)執(zhí)行 effect。

對(duì)比的邏輯在 areHookInputsEqual 這個(gè)函數(shù)里:

如果 prevDeps 是 null,那就直接返回 false,這就是 useEffect 第二個(gè)參數(shù)傳 undefined 或者 null 的話 effect 函數(shù)都會(huì)執(zhí)行的原因。

否則,才會(huì)新舊的 deps 數(shù)組中每個(gè)元素做對(duì)比,有一個(gè)不一樣就返回 false。

這已經(jīng)解釋了上面那個(gè)案例,deps 數(shù)組傳 undefined、[]、[dep] 時(shí) effect 執(zhí)行的不同情況。

其實(shí)還有一種情況也會(huì)導(dǎo)致 effect 執(zhí)行,就是上面這段邏輯:

當(dāng)熱更新的時(shí)候,就算依賴沒有變,也需要重新執(zhí)行 effect,這個(gè)是通過 ignorePreviousDependencies 變量來控制的。

這個(gè)估計(jì)很多人都不知道,因?yàn)闊岣率枪ぞ邔?shí)現(xiàn)的。

我們從源碼層面解釋清楚了 useEffect 第二個(gè)參數(shù)的處理機(jī)制。

其實(shí) useCallback、useMemo 的 deps 參數(shù)處理邏輯也是一樣的,源碼都差不多:

總結(jié)

useEffect 第二個(gè)參數(shù)傳入 undefined、[]、[a,b,c] 時(shí)執(zhí)行的效果不同, undefined 每次都會(huì)執(zhí)行,而依賴數(shù)組只有在依賴變了才會(huì)執(zhí)行,空數(shù)組只會(huì)執(zhí)行一次。

我們從源碼層面解釋了原因:

hooks 是在 fiber 節(jié)點(diǎn)的 memorizedState 屬性上存取數(shù)據(jù)的,會(huì)組織一個(gè)和 hook 一一對(duì)應(yīng)的鏈表。

構(gòu)建這個(gè)鏈表的階段叫 mount,后面只需要 update,所以所有的 hook 的實(shí)現(xiàn)都分為了 mountXxx 和 updateXxx 兩部分。

useEffect 在 update 時(shí)會(huì)對(duì)比新傳入的 deps 和之前存在 memorizedState 上的 deps 來確定是否執(zhí)行 effect 回調(diào),它做了這樣的處理:

當(dāng) dep 是 null(undefined 也會(huì)處理成 null)時(shí),判定為不相等。如果是熱更新的時(shí)候,判定為不相等。否則會(huì)對(duì)比數(shù)組的每個(gè)依賴項(xiàng)來判斷是否相等。只要新舊 deps 不相等就執(zhí)行 effect。

useCallback、useMemo 的 deps 處理也是一樣的,我們從源碼層面理清楚了 deps 參數(shù)的處理機(jī)制。


文章題目:從源碼理清UseEffect第二個(gè)參數(shù)是怎么處理的
文章來源:http://www.dlmjj.cn/article/dpesgjc.html