日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

面試官:useEffect和useLayoutEffect有什么區(qū)別?你能說說嗎?

來源: 責(zé)編: 時間:2023-12-13 17:01:27 226觀看
導(dǎo)讀Effect數(shù)據(jù)結(jié)構(gòu)顧名思義,React底層在函數(shù)式組件的Fiber節(jié)點設(shè)計中帶入了hooks鏈表的概念(memorizedState),在此變量上專門存儲每一個函數(shù)式組件對應(yīng)的鏈表。而對于副作用(useEffect or useLayoutEffect)來說,對應(yīng)其hook類型

Effect數(shù)據(jù)結(jié)構(gòu)

顧名思義,React底層在函數(shù)式組件的Fiber節(jié)點設(shè)計中帶入了hooks鏈表的概念(memorizedState),在此變量上專門存儲每一個函數(shù)式組件對應(yīng)的鏈表。bxC28資訊網(wǎng)——每日最新資訊28at.com

而對于副作用(useEffect or useLayoutEffect)來說,對應(yīng)其hook類型就是Effect。bxC28資訊網(wǎng)——每日最新資訊28at.com

單個的effect對象包括以下幾個屬性:bxC28資訊網(wǎng)——每日最新資訊28at.com

  • create: 傳入useEffect or useLayoutEffect函數(shù)的第一個參數(shù),即回調(diào)函數(shù);
  • destroy: 回調(diào)函數(shù)return的函數(shù),在該effect銷毀的時候執(zhí)行,渲染階段為undefined;
  • deps: 依賴項,改變重新執(zhí)行副作用;
  • next: 指向下一個effect;
  • tag: effect的類型,區(qū)分是useEffect還是useLayoutEffect;

單純看這些字段,和平時使用層面來聯(lián)想還是很通俗易懂的,這里還是補充一下hooks鏈表的概念,有如下的例子:bxC28資訊網(wǎng)——每日最新資訊28at.com

const Hello = () => {    const [ text, setText ] = useState('hello')    useEffect(() => {        console.log('effect1')        return () => {            console.log('destory1');        }    })    useLayoutEffect(() => {        console.log('effect2')        return () => {            console.log('destory2');        }    })    return <div>effect</div>}

掛載到Hello組件fiber上memoizedState如下:bxC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片bxC28資訊網(wǎng)——每日最新資訊28at.com

可以看到,打印出來結(jié)果和組件中聲明hook的順序是一樣的,不難看出這是一個鏈表,這也是為什么react hook要求hook的使用不能放在條件分支語句中的原因,如果第一次mount走的是A情況,第二次updateMount走的是B情況,就會出現(xiàn)hooks鏈表混亂的情況,保證官方范式是比較重要的原因。bxC28資訊網(wǎng)——每日最新資訊28at.com

Hook

從上圖的例子中可以看到,memorizedState的值會根據(jù)不同hook來決定。bxC28資訊網(wǎng)——每日最新資訊28at.com

  • 使用useState時,memorizedState對應(yīng)是string(hello);
  • 使用useEffect和useLayoutEffect,對應(yīng)的是Effect;

Hook類型如下:bxC28資訊網(wǎng)——每日最新資訊28at.com

export type Hook = {     memoizedState: any, // Hook 自身維護的狀態(tài)     baseQueue: any,    baseState: any,    queue: UpdateQueue<any, any> | null, // Hook 自身維護的更新隊列     next: Hook | null, // next 指向下一個 Hook };

創(chuàng)建副作用流程

基于上面的數(shù)據(jù)結(jié)構(gòu),對于use(Layout)Effect來說,React做的事情就是bxC28資訊網(wǎng)——每日最新資訊28at.com

  • render階段:函數(shù)組件開始渲染的時候,創(chuàng)建出對應(yīng)的hook鏈表掛載到workInProgress的memoizedState上,并創(chuàng)建effect鏈表,也就是掛載到對應(yīng)的fiber節(jié)點上,但是基于上次和本次依賴項的比較結(jié)果, 創(chuàng)建的effect是有差異的。這一點暫且可以理解為:依賴項有變化,effect可以被處理,否則不會被處理。
  • commit階段:異步調(diào)度useEffect或者同步處理useLayoutEffect的effect。等到commit階段完成后,更新應(yīng)用到頁面上之后,開始處理useEffect產(chǎn)生的effect,或是直接處理commit階段同步執(zhí)行阻塞頁面更新的useLayoutEffect產(chǎn)生的effect。

第二點提到了一個重點,就是useEffect和useLayoutEffect的執(zhí)行時機不一樣,前者被異步調(diào)度,當(dāng)頁面渲染完成后再去執(zhí)行,不會阻塞頁面渲染。 后者是在commit階段新的DOM準備完成,但還未渲染到屏幕之前,同步執(zhí)行。bxC28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建effect鏈表

useEffect的工作是在currentlyRenderingFiber加載當(dāng)前的hook,具體流程就是判斷當(dāng)前fiber是否已經(jīng)存在hook(就是判斷fiber.memoizedState),存在的話則創(chuàng)建一個effect hook到鏈表的最后,也就是.next,沒有的話則創(chuàng)建一個memoizedState。bxC28資訊網(wǎng)——每日最新資訊28at.com

先看一下創(chuàng)建一個Effect的入口函數(shù):bxC28資訊網(wǎng)——每日最新資訊28at.com

function mountEffect(    create: () => (() => void) | void,    deps: Array<mixed> | void | null): void {    return mountEffectImpl(        UpdateEffect | PassiveEffect,        HookPassive,        create,        deps,    );};

可以看到本質(zhì)上是調(diào)用了mountEffectImpl函數(shù),傳了上一節(jié)所說的Effect type中的字段,這里有個問題,為什么destroy沒傳呢?獲取上一次effect的destroy函數(shù),也就是useEffect回調(diào)中return的函數(shù),在創(chuàng)建階段是第一次,所以為undefined。bxC28資訊網(wǎng)——每日最新資訊28at.com

這里看一下創(chuàng)建階段調(diào)用的mountEffectImpl函數(shù):bxC28資訊網(wǎng)——每日最新資訊28at.com

function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {  // 創(chuàng)建hook對象  const hook = mountWorkInProgressHook();  // 獲取依賴  const nextDeps = deps === undefined ? null : deps;  // 為fiber打上副作用的effectTag  currentlyRenderingFiber.flags |= fiberFlags;  // 創(chuàng)建effect鏈表,掛載到hook的memoizedState上和fiber的updateQueue  hook.memoizedState = pushEffect(    HookHasEffect | hookFlags,    create,    undefined,    nextDeps,  );}

接下來我們都知道,React或Vue都是狀態(tài)改變導(dǎo)致頁面重渲染,而useEffect or useLayoutEffect都會會根據(jù)deps變化重新執(zhí)行,所以猜都猜得到,在更新時調(diào)用的updateEffectImpl函數(shù),對比mountEffectImpl函數(shù)多出來的一部分內(nèi)容其實就是對比上一次的Effect的依賴變化,以及執(zhí)行上一次Effect中的destroy部分內(nèi)容~代碼如下:bxC28資訊網(wǎng)——每日最新資訊28at.com

function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {  const hook = updateWorkInProgressHook();  const nextDeps = deps === undefined ? null : deps;  let destroy = undefined;  if (currentHook !== null) {    // 從currentHook中獲取上一次的effect    const prevEffect = currentHook.memoizedState;    // 獲取上一次effect的destory函數(shù),也就是useEffect回調(diào)中return的函數(shù)    destroy = prevEffect.destroy;    if (nextDeps !== null) {      const prevDeps = prevEffect.deps;      // 比較前后依賴,push一個不帶HookHasEffect的effect      if (areHookInputsEqual(nextDeps, prevDeps)) {        pushEffect(hookFlags, create, destroy, nextDeps);        return;      }    }  }  currentlyRenderingFiber.flags |= fiberFlags;  // 如果前后依賴有變,在effect的tag中加入HookHasEffect  // 并將新的effect更新到hook.memoizedState上  hook.memoizedState = pushEffect(    HookHasEffect | hookFlags,    create,    destroy,    nextDeps,  );}

可以看到在mountEffectImpl和updateEffectImpl中,最后的結(jié)果走向都是pushEffect函數(shù),它的工作很純粹,就是創(chuàng)建出effect對象,把對象掛到鏈表中。bxC28資訊網(wǎng)——每日最新資訊28at.com

pushEffect代碼如下:bxC28資訊網(wǎng)——每日最新資訊28at.com

function pushEffect(tag, create, destroy, deps) {  // 創(chuàng)建effect對象  const effect: Effect = {    tag,    create,    destroy,    deps,    // Circular    next: (null: any),  };  // 從workInProgress節(jié)點上獲取到updateQueue,為構(gòu)建鏈表做準備  let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);  if (componentUpdateQueue === null) {    // 如果updateQueue為空,把effect放到鏈表中,和它自己形成閉環(huán)    componentUpdateQueue = createFunctionComponentUpdateQueue();    // 將updateQueue賦值給WIP節(jié)點的updateQueue,實現(xiàn)effect鏈表的掛載    currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);    componentUpdateQueue.lastEffect = effect.next = effect;  } else {    // updateQueue不為空,將effect接到鏈表的后邊    const lastEffect = componentUpdateQueue.lastEffect;    if (lastEffect === null) {      componentUpdateQueue.lastEffect = effect.next = effect;    } else {      const firstEffect = lastEffect.next;      lastEffect.next = effect;      effect.next = firstEffect;      componentUpdateQueue.lastEffect = effect;    }  }  return effect;}

這里的主要邏輯其實就是本節(jié)開頭所說的,區(qū)分兩種情況,鏈表為空或鏈表存在的情況,值得一提的是這里的updateQueue是一個環(huán)形鏈表。bxC28資訊網(wǎng)——每日最新資訊28at.com

以上,就是effect鏈表的構(gòu)建過程。我們可以看到,effect對象創(chuàng)建出來最終會以兩種形式放到兩個地方:單個的effect,放到hook.memorizedState上;環(huán)狀的effect鏈表,放到fiber節(jié)點的updateQueue中。兩者各有用途,前者的effect會作為上次更新的effect,為本次創(chuàng)建effect對象提供參照(對比依賴項數(shù)組),后者的effect鏈表會作為最終被執(zhí)行的主體,帶到commit階段處理。bxC28資訊網(wǎng)——每日最新資訊28at.com

提交階段

commitRoot

當(dāng)我們完成更新,進入提交重渲染視圖時,主要在commitRoot函數(shù)中執(zhí)行,而在這之前創(chuàng)建Effect以及插入到hooks鏈表中,useEffect和useLayoutEffect其實做的都是一樣的,也是共用的,在提交階段,我們會看出兩者執(zhí)行時機不同的實現(xiàn)點。bxC28資訊網(wǎng)——每日最新資訊28at.com

// src/react-reconciler/src/ReactFiberWorkLoop.jsfunction commitRoot(root) {  // 已經(jīng)完成構(gòu)建的fiber,上面會包括hook信息  const { finishedWork } = root;  // 如果存在useEffect或者useLayoutEffect  if ((finishedWork.flags & Passive) !== NoFlags) {    if (!rootDoesHavePassiveEffect) {      rootDoesHavePassiveEffect = true;      // 開啟下一個宏任務(wù)      requestIdleCallback(flushPassiveEffect);    }  }  console.log('start commit.');    // 判斷自己身上有沒有副作用  const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;  // 如果自己的副作用或者子節(jié)點有副作用就進行DOM操作  if (rootHasEffect) {    console.log('DOM執(zhí)行完畢');      commitMutationEffectsOnFiber(finishedWork, root);        // 執(zhí)行l(wèi)ayout Effect      console.log('開始執(zhí)行l(wèi)ayoutEffect');    commitLayoutEffects(finishedWork, root);    if (rootDoesHavePassiveEffect) {      rootDoesHavePassiveEffect = false;      rootWithPendingPassiveEffects = root;    }  }  // 等DOM變更之后,更改root中current的指向  root.current = finishedWork;}

這里的rootDoesHavePassiveEffect是核心判斷點,還記得Effect類型中的tag參數(shù)嗎?就是依靠這個參數(shù)來標(biāo)識區(qū)分useEffect和useLayoutEffect的。bxC28資訊網(wǎng)——每日最新資訊28at.com

rootDoesHavePassiveEffect === false,則執(zhí)行宏任務(wù),將Effect副作用推入宏任務(wù)執(zhí)行棧中。我們可以簡單理解成useEffect的回調(diào)函數(shù)包裝在了requestIdleCallback中去異步執(zhí)行,根據(jù)fiber的知識接下來會去走瀏覽器當(dāng)前幀是否有空余時間來判斷副作用函數(shù)的執(zhí)行時機。bxC28資訊網(wǎng)——每日最新資訊28at.com

繼續(xù)往下走,如果rootHasEffect === true,代表有副作用,如果是useEffect,副作用已經(jīng)在上面進入宏任務(wù)隊列了,所以如果是useLayoutEffect,就會在這個條件中去執(zhí)行,所以在這里我們可以理解到那一句"useEffect和useLayoutEffect的區(qū)別是,前者會異步執(zhí)行副作用函數(shù)不會阻塞頁面更新,后者會立即執(zhí)行副作用函數(shù),會阻塞頁面更新,不適合寫入復(fù)雜邏輯。"的原因了。bxC28資訊網(wǎng)——每日最新資訊28at.com

結(jié)尾

useEffect與useLayoutEffect十分相似,就連簽名都一樣,不同之處就在于前者會在瀏覽器繪制后延遲執(zhí)行,而后者會在所有DOM變更之后同步調(diào)用effect,希望你看到這里,可以對于這個結(jié)論的來源有一定的了解和學(xué)習(xí),希望可以幫到你~bxC28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-44381-0.html面試官:useEffect和useLayoutEffect有什么區(qū)別?你能說說嗎?

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 改善代碼質(zhì)量,試試這十種方法

下一篇: Python設(shè)計模式,裝飾器模式

標(biāo)簽:
  • 熱門焦點
  • K60 Pro官方停產(chǎn) 第三方瞬間漲價

    雖然沒有官方宣布,但Redmi的一些高管也已經(jīng)透露了,Redmi K60 Pro已經(jīng)停產(chǎn)且不會補貨,這一切都是為了即將到來的K60 Ultra鋪路,屬于廠家的正常操作。但有意思的是該機在停產(chǎn)之后
  • 小米降噪藍牙耳機Necklace分享:聽一首歌 讀懂一個故事

    在今天下午的小米Civi 2新品發(fā)布會上,小米還帶來了一款新的降噪藍牙耳機Necklace,我們也在發(fā)布結(jié)束的第一時間給大家?guī)磉@款耳機的簡單分享。現(xiàn)在大家能見到最多的藍牙耳機
  • 5月安卓手機好評榜:魅族20 Pro奪冠

    性能榜和性價比榜之后,我們來看最后的安卓手機好評榜,數(shù)據(jù)來源安兔兔評測,收集時間2023年5月1日至5月31日,僅限國內(nèi)市場。第一名:魅族20 Pro好評率:97.50%不得不感慨魅族老品牌還
  • Raft算法:保障分布式系統(tǒng)共識的穩(wěn)健之道

    1. 什么是Raft算法?Raft 是英文”Reliable、Replicated、Redundant、And Fault-Tolerant”(“可靠、可復(fù)制、可冗余、可容錯”)的首字母縮寫。Raft算法是一種用于在分布式系統(tǒng)
  • 十個可以手動編寫的 JavaScript 數(shù)組 API

    JavaScript 中有很多API,使用得當(dāng),會很方便,省力不少。 你知道它的原理嗎? 今天這篇文章,我們將對它們進行一次小總結(jié)。現(xiàn)在開始吧。1.forEach()forEach()用于遍歷數(shù)組接收一參
  • 多線程開發(fā)帶來的問題與解決方法

    使用多線程主要會帶來以下幾個問題:(一)線程安全問題  線程安全問題指的是在某一線程從開始訪問到結(jié)束訪問某一數(shù)據(jù)期間,該數(shù)據(jù)被其他的線程所修改,那么對于當(dāng)前線程而言,該線程
  • 馮提莫簽約抖音公會 前“斗魚一姐”消失在直播間

    來源:直播觀察提起&ldquo;馮提莫&rdquo;這個名字,很多網(wǎng)友或許聽過,但應(yīng)該不記得她是哪位主播了。其實,作為曾經(jīng)的&ldquo;斗魚一姐&rdquo;,馮提莫在游戲直播的年代影響力不輸于現(xiàn)
  • 朋友圈可以修改可見范圍了 蘋果用戶可率先體驗

    近日,iOS用戶迎來微信8.0.27正式版更新,除了可更換二維碼背景外,還新增了多項實用功能。在新版微信中,朋友圈終于可以修改可見范圍,簡單來說就是已發(fā)布的朋友圈
  • 北京:科技教育體驗基地開始登記

      北京“科技館之城”科技教育體驗基地登記和認證工作日前啟動。首批北京科技教育體驗基地擬于2023年全國科普日期間掛牌,后續(xù)還將開展常態(tài)化登記。  北京科技教育體驗基
Top 主站蜘蛛池模板: 东兰县| 宁强县| 浦江县| 读书| 仁布县| 石台县| 三门县| 文登市| 邢台县| 武安市| 汾阳市| 罗江县| 永丰县| 长海县| 柯坪县| 独山县| 桐庐县| 雅安市| 仙居县| 南丰县| 清远市| 大安市| 安岳县| 融水| 江西省| 揭东县| 宝兴县| 桃江县| 江北区| 屏边| 德惠市| 赣榆县| 明光市| 滁州市| 茌平县| 科技| 瓮安县| 会泽县| 连云港市| 徐水县| 铜梁县|