精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發 前端
useEffect與useLayoutEffect?十分相似,就連簽名都一樣,不同之處就在于前者會在瀏覽器繪制后延遲執行,而后者會在所有DOM變更之后同步調用effect,希望你看到這里,可以對于這個結論的來源有一定的了解和學習,希望可以幫到你~

Effect數據結構

顧名思義,React底層在函數式組件的Fiber節點設計中帶入了hooks鏈表的概念(memorizedState),在此變量上專門存儲每一個函數式組件對應的鏈表。

而對于副作用(useEffect or useLayoutEffect)來說,對應其hook類型就是Effect。

單個的effect對象包括以下幾個屬性:

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

單純看這些字段,和平時使用層面來聯想還是很通俗易懂的,這里還是補充一下hooks鏈表的概念,有如下的例子:

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如下:

圖片圖片

可以看到,打印出來結果和組件中聲明hook的順序是一樣的,不難看出這是一個鏈表,這也是為什么react hook要求hook的使用不能放在條件分支語句中的原因,如果第一次mount走的是A情況,第二次updateMount走的是B情況,就會出現hooks鏈表混亂的情況,保證官方范式是比較重要的原因。

Hook

從上圖的例子中可以看到,memorizedState的值會根據不同hook來決定。

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

Hook類型如下:

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

創建副作用流程

基于上面的數據結構,對于use(Layout)Effect來說,React做的事情就是

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

第二點提到了一個重點,就是useEffect和useLayoutEffect的執行時機不一樣,前者被異步調度,當頁面渲染完成后再去執行,不會阻塞頁面渲染。 后者是在commit階段新的DOM準備完成,但還未渲染到屏幕之前,同步執行。

創建effect鏈表

useEffect的工作是在currentlyRenderingFiber加載當前的hook,具體流程就是判斷當前fiber是否已經存在hook(就是判斷fiber.memoizedState),存在的話則創建一個effect hook到鏈表的最后,也就是.next,沒有的話則創建一個memoizedState。

先看一下創建一個Effect的入口函數:

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

可以看到本質上是調用了mountEffectImpl函數,傳了上一節所說的Effect type中的字段,這里有個問題,為什么destroy沒傳呢?獲取上一次effect的destroy函數,也就是useEffect回調中return的函數,在創建階段是第一次,所以為undefined。

這里看一下創建階段調用的mountEffectImpl函數:

function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
  // 創建hook對象
  const hook = mountWorkInProgressHook();
  // 獲取依賴
  const nextDeps = deps === undefined ? null : deps;

  // 為fiber打上副作用的effectTag
  currentlyRenderingFiber.flags |= fiberFlags;

  // 創建effect鏈表,掛載到hook的memoizedState上和fiber的updateQueue
  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    create,
    undefined,
    nextDeps,
  );
}

接下來我們都知道,React或Vue都是狀態改變導致頁面重渲染,而useEffect or useLayoutEffect都會會根據deps變化重新執行,所以猜都猜得到,在更新時調用的updateEffectImpl函數,對比mountEffectImpl函數多出來的一部分內容其實就是對比上一次的Effect的依賴變化,以及執行上一次Effect中的destroy部分內容~代碼如下:

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函數,也就是useEffect回調中return的函數
    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中,最后的結果走向都是pushEffect函數,它的工作很純粹,就是創建出effect對象,把對象掛到鏈表中。

pushEffect代碼如下:

function pushEffect(tag, create, destroy, deps) {
  // 創建effect對象
  const effect: Effect = {
    tag,
    create,
    destroy,
    deps,
    // Circular
    next: (null: any),
  };

  // 從workInProgress節點上獲取到updateQueue,為構建鏈表做準備
  let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
  if (componentUpdateQueue === null) {
    // 如果updateQueue為空,把effect放到鏈表中,和它自己形成閉環
    componentUpdateQueue = createFunctionComponentUpdateQueue();
    // 將updateQueue賦值給WIP節點的updateQueue,實現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;
}

這里的主要邏輯其實就是本節開頭所說的,區分兩種情況,鏈表為空或鏈表存在的情況,值得一提的是這里的updateQueue是一個環形鏈表。

以上,就是effect鏈表的構建過程。我們可以看到,effect對象創建出來最終會以兩種形式放到兩個地方:單個的effect,放到hook.memorizedState上;環狀的effect鏈表,放到fiber節點的updateQueue中。兩者各有用途,前者的effect會作為上次更新的effect,為本次創建effect對象提供參照(對比依賴項數組),后者的effect鏈表會作為最終被執行的主體,帶到commit階段處理。

提交階段

commitRoot

當我們完成更新,進入提交重渲染視圖時,主要在commitRoot函數中執行,而在這之前創建Effect以及插入到hooks鏈表中,useEffect和useLayoutEffect其實做的都是一樣的,也是共用的,在提交階段,我們會看出兩者執行時機不同的實現點。

// src/react-reconciler/src/ReactFiberWorkLoop.js
function commitRoot(root) {
  // 已經完成構建的fiber,上面會包括hook信息
  const { finishedWork } = root;

  // 如果存在useEffect或者useLayoutEffect
  if ((finishedWork.flags & Passive) !== NoFlags) {
    if (!rootDoesHavePassiveEffect) {
      rootDoesHavePassiveEffect = true;
      // 開啟下一個宏任務
      requestIdleCallback(flushPassiveEffect);
    }
  }

  console.log('start commit.');
  
  // 判斷自己身上有沒有副作用
  const rootHasEffect = (finishedWork.flags & MutationMask) !== NoFlags;
  // 如果自己的副作用或者子節點有副作用就進行DOM操作
  if (rootHasEffect) {
    console.log('DOM執行完畢');  
    commitMutationEffectsOnFiber(finishedWork, root);  
  
    // 執行layout Effect  
    console.log('開始執行layoutEffect');
    commitLayoutEffects(finishedWork, root);
    if (rootDoesHavePassiveEffect) {
      rootDoesHavePassiveEffect = false;
      rootWithPendingPassiveEffects = root;
    }
  }
  // 等DOM變更之后,更改root中current的指向
  root.current = finishedWork;
}

這里的rootDoesHavePassiveEffect是核心判斷點,還記得Effect類型中的tag參數嗎?就是依靠這個參數來標識區分useEffect和useLayoutEffect的。

rootDoesHavePassiveEffect === false,則執行宏任務,將Effect副作用推入宏任務執行棧中。我們可以簡單理解成useEffect的回調函數包裝在了requestIdleCallback中去異步執行,根據fiber的知識接下來會去走瀏覽器當前幀是否有空余時間來判斷副作用函數的執行時機。

繼續往下走,如果rootHasEffect === true,代表有副作用,如果是useEffect,副作用已經在上面進入宏任務隊列了,所以如果是useLayoutEffect,就會在這個條件中去執行,所以在這里我們可以理解到那一句"useEffect和useLayoutEffect的區別是,前者會異步執行副作用函數不會阻塞頁面更新,后者會立即執行副作用函數,會阻塞頁面更新,不適合寫入復雜邏輯。"的原因了。

結尾

useEffect與useLayoutEffect十分相似,就連簽名都一樣,不同之處就在于前者會在瀏覽器繪制后延遲執行,而后者會在所有DOM變更之后同步調用effect,希望你看到這里,可以對于這個結論的來源有一定的了解和學習,希望可以幫到你~

責任編輯:武曉燕 來源: 量子前端
相關推薦

2023-12-25 15:40:55

React開發

2023-02-17 08:10:24

2024-04-03 15:33:04

JWTSession傳輸信息

2024-09-19 08:42:43

2025-04-01 00:00:00

項目CRUD單例模式

2023-10-27 15:31:04

For循環Foreach循環

2021-11-30 07:44:50

FinalFinallyFinalize

2021-12-10 12:01:37

finalfinallyfinalize

2024-03-20 15:12:59

KafkaES中間件

2021-12-13 06:56:45

Comparable元素排序

2021-12-23 07:11:31

開發

2021-12-27 06:57:40

This SuperJava

2023-02-09 07:01:35

轉發重定向Java

2023-07-11 08:40:02

IO模型后臺

2022-12-05 08:12:31

net/http庫http連接池

2021-07-08 06:51:29

React函數組件

2022-05-16 11:04:43

RocketMQPUSH 模式PULL 模式

2023-02-20 07:19:14

2024-03-26 16:24:46

分布式事務2PC3PC

2021-08-10 08:34:12

Git ForkBranch
點贊
收藏

51CTO技術棧公眾號

波多野结衣一区二区三区在线| 久久久久久久久久久国产精品| 亚洲男同gay网站| 成人涩涩免费视频| 国产999在线观看| 天天色影综合网| 欧美偷窥清纯综合图区| 欧美日本韩国一区| 日本免费黄视频| 黄色网在线免费观看| 99久久久精品免费观看国产蜜| 国产欧美日韩综合精品| 国产无码精品在线观看| 欧美激情偷拍自拍| 日韩精品视频免费| 无码人妻少妇色欲av一区二区| 国模一区二区| 午夜欧美大尺度福利影院在线看| 亚欧洲精品在线视频免费观看| www.日日夜夜| 精品亚洲国产成人av制服丝袜 | 欧美日韩小视频| 久久国产精品视频在线观看| gogo在线高清视频| 中文字幕欧美日韩一区| 国产麻豆乱码精品一区二区三区| 一卡二卡在线观看| 久久精品麻豆| 91成品人片a无限观看| 青青草手机视频在线观看| 黑人操亚洲人| 亚洲欧美综合图区| 日本一卡二卡在线| 91成人午夜| 日韩欧美一区二区视频| 久国产精品视频| 成人国产精品入口免费视频| 日韩欧美在线字幕| 无码aⅴ精品一区二区三区浪潮| 制服丝袜中文字幕在线| 亚洲乱码中文字幕| 久久久一二三四| 快射视频在线观看| 中文字幕一区二区三区视频| 亚洲精品久久区二区三区蜜桃臀| 免费在线黄色电影| 久久久久久久综合日本| 免费中文日韩| 裸体xxxx视频在线| 国产三级一区二区| 日本在线播放一区| 国产日产精品久久久久久婷婷| 91丨九色丨黑人外教| 国产伦精品一区二区三毛| 嫩草影院一区二区| 99久久99精品久久久久久 | 久久超碰99| 亚洲欧美国产日韩天堂区| 中文字字幕码一二三区| 久久99国内| 中文字幕亚洲无线码在线一区| 亚洲成人黄色av| 欧美大片aaaa| 欧美精品制服第一页| 国产一级性生活| 国产日韩欧美| 国产精品久久久久久中文字| 亚洲天堂久久久久| 国产一区二区三区免费看| 7777精品伊久久久大香线蕉语言 | 97se亚洲国产综合在线| 乱一区二区三区在线播放| 美丽的姑娘在线观看免费动漫| 久久久久久久久99精品| 在线看成人av电影| 日本孕妇大胆孕交无码| 欧美性黄网官网| 国产精品久久久毛片| 四虎地址8848精品| 精品日韩99亚洲| 久久久久久久久久久国产精品| 成人vr资源| 欧美久久精品午夜青青大伊人| 久久精品女人毛片国产| 日韩黄色免费网站| 99re在线观看| 国产一级免费在线观看| 日韩久久一区二区| 极品美女扒开粉嫩小泬| 欧美黄色网络| 日韩av一区二区在线| 国产18无套直看片| 亚洲大片av| 国产精品你懂得| 好男人在线视频www| 欧美经典三级视频一区二区三区| 亚洲五码在线观看视频| 亚洲成人人体| 精品88久久久久88久久久| 熟女少妇内射日韩亚洲| 欧美视频二区| 国产日韩欧美影视| 色播色播色播色播色播在线 | 老司机av福利| 韩国主播福利视频一区二区三区| 91精品国产综合久久精品性色| 一级国产黄色片| 国产精品99久久| 日韩美女毛茸茸| 欧美在线精品一区二区三区| 中文字幕乱码一区二区免费| 免费国产a级片| 欧美成人精品一级| 日韩中文理论片| 中文字幕在线看人| 成人av在线资源网站| www.黄色网址.com| 成人1区2区| 亚洲欧美一区二区三区四区| 精品肉丝脚一区二区三区| 久久er99精品| 水蜜桃一区二区| 久久精品女人天堂av免费观看| 亚洲激情视频在线| 久久精品久久国产| 国产福利精品一区二区| 日日噜噜噜夜夜爽爽| 99riav视频一区二区| 亚洲精品中文字| 久久精品视频1| 91在线精品一区二区| www.在线观看av| 日韩欧美激情电影| 九九热精品视频国产| 国产麻豆精品一区| 中文字幕日韩精品一区| 视色视频在线观看| 精品国产乱码久久久久久1区2匹| 日本老师69xxx| 久久久久久青草| 在线精品视频免费播放| 精品无码在线观看| 免费观看日韩av| 中文字幕在线亚洲三区| 国产一精品一av一免费爽爽| 久久久精品网站| 99视频免费看| 亚洲一级二级在线| 亚洲午夜久久久久久久久| 国内精品美女在线观看| 国产精华一区二区三区| www.综合| 亚洲精品一二区| 日韩精品一区不卡| 国产精品国产自产拍高清av| 中文字幕一区久久| 国产精品草草| 久久大香伊蕉在人线观看热2| 都市激情综合| 中文精品99久久国产香蕉| 亚洲中文一区二区三区| 亚洲欧美日韩国产综合| 年下总裁被打光屁股sp| 亚洲全部视频| 欧美一区免费视频| 亚洲网站三级| 久久久久久久久久国产精品| 欧美在线 | 亚洲| 91国偷自产一区二区开放时间| 国产精品情侣呻吟对白视频| 韩国精品久久久| 一二三四视频社区在线| 国产精品欧美在线观看| 91在线免费视频| 女人让男人操自己视频在线观看| 亚洲网站在线看| 国产高清免费在线观看| 懂色av一区二区三区| 少妇愉情理伦三级| 成人一区二区视频| 男女无套免费视频网站动漫| 影音先锋日韩精品| 久久精品国产一区二区三区日韩 | 日韩一级欧美一级| aaa人片在线| 1024国产精品| 欧美做受喷浆在线观看| 久久国产剧场电影| 国产一区二区网| 亚洲视频电影在线| 欧美日韩精品综合| 日韩欧美中文字幕一区二区三区| 日韩美女免费视频| 污片在线免费观看| 中文国产成人精品| 亚洲 另类 春色 国产| 欧美精品在线视频| 日韩在线视频不卡| 一区二区三区 在线观看视频| 欧美成人国产精品一区二区| 成人一区二区三区在线观看| 亚洲 欧美 日韩系列| aa国产精品| 超级碰在线观看| 成人直播大秀| 欧美精品欧美精品| 中文一区二区三区四区| 成人黄色av免费在线观看| 免费成人直播| 国内精品免费午夜毛片| h片在线免费观看| 中文字幕成人在线| 欧美亚洲日本| 亚洲国产精品视频在线观看| 国产美女永久免费| 欧美性xxxxx极品少妇| 日本高清www免费视频| 有坂深雪av一区二区精品| 战狼4完整免费观看在线播放版| av电影在线观看完整版一区二区| 成人三级做爰av| 精品一区精品二区高清| 手机视频在线观看| 日韩极品在线观看| 99久久国产宗和精品1上映| 国产日韩精品视频一区二区三区| 国产亚洲黄色片| 国产精品hd| 大荫蒂性生交片| 欧美涩涩视频| 亚洲色成人www永久在线观看 | caopor在线视频| 久久婷婷久久| 欧美日韩第二页| 久久一区视频| 欧美 日韩精品| 免费国产自线拍一欧美视频| 无码人妻丰满熟妇区96| 99精品热6080yy久久| 婷婷无套内射影院| 一区二区国产在线观看| 大陆极品少妇内射aaaaa| 国产欧美综合一区二区三区| 99精品人妻少妇一区二区| 一区二区三区国产在线| 国产二区视频在线播放| 日韩精品视频网| 天天色综合天天色| 免费成人在线网站| 日韩成人精品视频在线观看| 国产一区二区三区香蕉| 久久精品aⅴ无码中文字字幕重口| 国产风韵犹存在线视精品| 蜜臀av粉嫩av懂色av| 91网页版在线| 极品人妻videosss人妻| 国产精品超碰97尤物18| 五月天激情丁香| 亚洲一二三区在线观看| 久久久久久久极品| 欧美吻胸吃奶大尺度电影| 国产一区二区三区三州| 精品精品国产高清a毛片牛牛| 日本高清视频网站| 亚洲无限av看| а√天堂8资源在线官网| 午夜精品久久久久久久白皮肤| 午夜激情在线播放| 国产精品91一区| 精品国产亚洲一区二区三区大结局 | 国产午夜精品免费一区二区三区 | 三级视频在线| 中文字幕日韩欧美| 怡红院红怡院欧美aⅴ怡春院| 欧美华人在线视频| 国产精品亚洲一区二区三区在线观看| 国产精品自产拍高潮在线观看| 玖玖精品一区| 日本精品二区| 国产精品v亚洲精品v日韩精品 | 成人在线观看你懂的| 美女视频免费一区| 日本道中文字幕| 国产精品沙发午睡系列990531| 免费人成在线观看| 欧美亚洲精品一区| 黄色av网址在线| 久久精品成人一区二区三区| heyzo在线播放| 国产主播精品在线| 天天做夜夜做人人爱精品 | 影音先锋在线播放| 国产精品久久久久久久久久久久久久| 精品亚洲二区| 亚洲 日韩 国产第一区| 亚洲美女黄色| 91网址在线观看精品| 国产亚洲精品7777| 日本特黄特色aaa大片免费| 欧美精品国产精品| 免费在线毛片| 久久久久久午夜| 亚洲青青一区| 日韩精品欧美专区| 国产亚洲一级| 免费看三级黄色片| 中文字幕欧美一| 国产又粗又猛又黄视频| 欧美精品一区二区三| av免费网站在线| 国产精品视频一区二区高潮| 青青草原在线亚洲| 欧美男女爱爱视频| 国产剧情一区二区| 天堂av免费在线| 欧美无乱码久久久免费午夜一区| 三级视频在线| 2018中文字幕一区二区三区| 综合激情久久| 久久久久久久久影视| 精品一区二区在线播放| 欧洲美熟女乱又伦| 日本电影亚洲天堂一区| 深夜福利视频在线免费观看| 久久久久国产精品免费| 日本免费精品| 国产精品视频一二三四区| 九色|91porny| 神马久久精品综合| 91精品久久久久久久99蜜桃 | 蜜臀久久99精品久久久| 欧美高清性猛交| 一区二区在线视频观看| 韩国无码av片在线观看网站| 国产一区二区三区av电影| 卡通动漫亚洲综合| 91精品国产欧美一区二区| jizz性欧美| 成人高清在线观看| 伊人蜜桃色噜噜激情综合| 人妻激情偷乱频一区二区三区 | 一级特黄色大片| 久久精品99无色码中文字幕| 高清一区二区三区av| 久久99国产精品一区| 国产一区不卡在线| 欧美日韩激情在线观看| 欧美大片国产精品| 国产精品一二三产区| 欧美二区在线| 日本aⅴ精品一区二区三区| 国产成人精品视频免费| 欧美群妇大交群中文字幕| 老司机福利在线视频| 翡翠波斯猫1977年美国| 亚洲人人精品| 亚洲国产天堂av| 91精品国产综合久久久久| 毛片网站在线看| 精品免费视频123区| 老司机久久99久久精品播放免费| 精品人体无码一区二区三区| 欧美美女一区二区| 欧美日韩色网| 欧美一区二区三区电影在线观看| 美女免费视频一区| 久久久久无码国产精品| 日韩大陆毛片av| 国产亚洲精品精品国产亚洲综合| 激情图片qvod| ww久久中文字幕| 91theporn国产在线观看| 欧美激情一区二区三级高清视频| 欧美天堂社区| 奇米视频7777| 天天免费综合色| 一级日本在线| 国产一区在线观| 久久国产夜色精品鲁鲁99| 激情五月婷婷小说| 亚洲午夜未删减在线观看| 欧美久久亚洲| 无码无遮挡又大又爽又黄的视频| 日韩一区中文字幕| 午夜成人免费影院| 91亚洲精品一区二区| 午夜在线一区| 全网免费在线播放视频入口| 亚洲精品视频在线观看视频| 国产亚洲字幕| 九九热免费精品视频| 亚洲国产你懂的| 思思99re6国产在线播放| 精品国产一区二区三区麻豆小说| 免费观看日韩av| 一级片视频在线观看| 欧美黑人xxxx| 偷偷www综合久久久久久久| 亚洲最大成人网站| 亚洲第一区第二区|