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

React?Fiber構(gòu)建completeWork源碼解析

 更新時間:2023年02月06日 09:59:38   作者:GW_劉振  
這篇文章主要為大家介紹了React?Fiber構(gòu)建completeWork源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

之前我們介紹了beginWork,react使用的是深度優(yōu)先遍歷算法,整個fiber的構(gòu)建都遵循此算法。

這也意味著,并不是所有節(jié)點beginWork完成后,才去進行completeWork。

當beginWork的next為null時,將進去completeWork。

一. completeUnitOfWork

function completeUnitOfWork(unitOfWork) {
  var completedWork = unitOfWork;
  do {
     // ...
     next = completeWork(current, completedWork, subtreeRenderLanes);
     // ...
     if (returnFiber !== null && (returnFiber.flags & Incomplete) === NoFlags) {
       if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = completedWork.firstEffect;
        }
        if (completedWork.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
              returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
            }
            returnFiber.lastEffect = completedWork.lastEffect;
        }
        var flags = completedWork.flags;
        if (flags > PerformedWork) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = completedWork;
          }else {
            returnFiber.firstEffect = completedWork;
          }
          returnFiber.lastEffect = completedWork;
        }
     }
     var siblingFiber = completedWork.sibling;
     if (siblingFiber !== null) {
        workInProgress = siblingFiber;
        return;
     }
     completedWork = returnFiber;
     workInProgress = completedWork;
  } while (completedWork !== null);
  // ...
}

根據(jù)深度優(yōu)先算法,當beginWork完成其中某個子樹干的最后一個節(jié)點時,進入completeUnitOfWork。根據(jù)最后的這個節(jié)點先完成completeWork,依次往上,直到找到相鄰節(jié)點。

核心方法分為2部分,其一:completeWork,其二:完成effect掛載,并串聯(lián)所有節(jié)點的effect(包括節(jié)點內(nèi)部的effect)組裝成環(huán)狀鏈表。

二. completeWork

function completeWork(current, workInProgress, renderLanes) {
    var newProps = workInProgress.pendingProps;
    switch (workInProgress.tag) {
      case IndeterminateComponent:
      case LazyComponent:
      case SimpleMemoComponent:
      case FunctionComponent:
      case ForwardRef:
      case Fragment:
      case Mode:
      case Profiler:
      case ContextConsumer:
      case MemoComponent:
        return null;
      case ClassComponent:
        // ...
      case HostRoot:
       // ...
        updateHostContainer(workInProgress);
        return null;
      case HostComponent:
        // ...
         var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
         appendAllChildren(instance, workInProgress, false, false);
         workInProgress.stateNode = instance;
        // ...
     // ...
}

這里很神奇的是,在react內(nèi)部updateHostContainer居然是個空函數(shù),也許后續(xù)版本做do something吧。 普通節(jié)點將進入HostComponent。

createInstance

function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
    var parentNamespace;
    {
      // TODO: take namespace into account when validating.
      var hostContextDev = hostContext;
      validateDOMNesting(type, null, hostContextDev.ancestorInfo);
      if (typeof props.children === 'string' || typeof props.children === 'number') {
        var string = '' + props.children;
        var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type);
        validateDOMNesting(null, string, ownAncestorInfo);
      }
      parentNamespace = hostContextDev.namespace;
    }
    var domElement = createElement(type, props, rootContainerInstance, parentNamespace);
    precacheFiberNode(internalInstanceHandle, domElement);
    updateFiberProps(domElement, props);
    return domElement;
  }

createElement

function createElement(type, props, rootContainerElement, parentNamespace) {
  // script標簽處理...
  // ...
  // flow相關webComponents處理
  // ...
  domElement = ownerDocument.createElement(type);
  // select標簽特殊處理,單獨設置multiple以及size
  // 非法標簽告警處理...
  return domElement;
}

到這里,我們可以看到開始創(chuàng)建每個fiber節(jié)點對應的dom對象了。但是并沒有插入到文檔流中。 那么真實的dom是如何連接到fiber對象呢?

precacheFiberNode,會在每個真實dom對象下,掛載對應的節(jié)點的fiber,precache是以_reactFiber$+隨機數(shù)的屬性。

updateFiberProps,會在每個真實dom對象下,掛載對應的props children即element對象,以_reactProps$+隨機數(shù)。

問題來了:每個fiber對應的真實dom對象,是單個構(gòu)建單個存儲?還是構(gòu)建一個總的dom樹?這之間是如何關聯(lián)起來的?

appendAllChildren

appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
    var node = workInProgress.child;
      while (node !== null) {
        if (node.tag === HostComponent || node.tag === HostText) {
          appendInitialChild(parent, node.stateNode);
        } else if (node.tag === HostPortal) ; else if (node.child !== null) {
          node.child.return = node;
          node = node.child;
          continue;
        }
        if (node === workInProgress) {
          return;
        }
        while (node.sibling === null) {
          if (node.return === null || node.return === workInProgress) {
            return;
          }
          node = node.return;
        }
        node.sibling.return = node.return;
        node = node.sibling;
      }
}

這里已經(jīng)很明顯了,根據(jù)之前創(chuàng)建的fiber鏈表,循環(huán)node節(jié)點,普通節(jié)點將調(diào)用appendInitialChild。即使用:parentInstance.appendChild(child); 至此可以看到循環(huán)結(jié)束后,生成了整個待插入的DOM節(jié)點(頁面首次渲染時)。

另外需要注意的是,根據(jù)fiber關聯(lián)dom也是在這個階段進行的(不是dom關聯(lián)fiber)

最后如何存在ref,當前的fiber樹對應的flags將和Ref的二進制數(shù)據(jù)取位運算或。(這很重要)

三. Effect

react推崇的是函數(shù)式編程,在一個函數(shù)組件里,如果存在useEffect等方法,那么react認為這是一個副作用函數(shù)組件。那么這些副作用是如何組織起來,又是在什么階段運行的呢?

早在beginWork,fiber的flags默認都是二進制0,如果存在副作用,如:useEffect,ref,useLayoutEffect等等,首次將被設置為Placement。但為什么存在useEffect函數(shù)組件的fiber對象,flags都是256以上的數(shù)值?

我們以useEffect為例,一探究竟。

useEffect

function useEffect(create, deps) {
    var dispatcher = resolveDispatcher();
    return dispatcher.useEffect(create, deps);
}

沒錯,useEffect方法定義就兩行代碼。

resolveDispatcher

function resolveDispatcher() {
    var dispatcher = ReactCurrentDispatcher.current;
    if (!(dispatcher !== null)) {
      {
        throw Error( "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem." );
      }
    }
    return dispatcher;
  }

我們繼續(xù)看下ReactCurrentDispatcher的定義:

const ReactCurrentDispatcher = {
  /**
   * @internal
   * @type {ReactComponent}
   */
  current: (null: null | Dispatcher),
};

最早的ReactCurrentDispatcher,是在renderRoot階段。如果root或Lane被改變了,原來的dispatch可能被置空了或首次不存在,使用當前的ContextOnlyDispatcher替代。

在函數(shù)組件beginWork階段,在執(zhí)行函數(shù)組件生成element對象之前,會賦值HooksDispatcherOnMount,這就是dispatch。

我們來看看HooksDispatcher:

{
      readContext: function (context, observedBits) {
        return readContext(context, observedBits);
      },
      useCallback: function (callback, deps) {
        currentHookNameInDev = 'useCallback';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountCallback(callback, deps);
      },
      useContext: function (context, observedBits) {
        currentHookNameInDev = 'useContext';
        mountHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect: function (create, deps) {
        currentHookNameInDev = 'useEffect';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountEffect(create, deps);
      },
      useImperativeHandle: function (ref, create, deps) {
        currentHookNameInDev = 'useImperativeHandle';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountImperativeHandle(ref, create, deps);
      },
      useLayoutEffect: function (create, deps) {
        currentHookNameInDev = 'useLayoutEffect';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountLayoutEffect(create, deps);
      },
      useMemo: function (create, deps) {
        currentHookNameInDev = 'useMemo';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        var prevDispatcher = ReactCurrentDispatcher$1.current;
        ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountMemo(create, deps);
        } finally {
          ReactCurrentDispatcher$1.current = prevDispatcher;
        }
      },
      useReducer: function (reducer, initialArg, init) {
        currentHookNameInDev = 'useReducer';
        mountHookTypesDev();
        var prevDispatcher = ReactCurrentDispatcher$1.current;
        ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher$1.current = prevDispatcher;
        }
      },
      useRef: function (initialValue) {
        currentHookNameInDev = 'useRef';
        mountHookTypesDev();
        return mountRef(initialValue);
      },
      useState: function (initialState) {
        currentHookNameInDev = 'useState';
        mountHookTypesDev();
        var prevDispatcher = ReactCurrentDispatcher$1.current;
        ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountState(initialState);
        } finally {
          ReactCurrentDispatcher$1.current = prevDispatcher;
        }
      },
      useDebugValue: function (value, formatterFn) {
        currentHookNameInDev = 'useDebugValue';
        mountHookTypesDev();
        return mountDebugValue();
      },
      useDeferredValue: function (value) {
        currentHookNameInDev = 'useDeferredValue';
        mountHookTypesDev();
        return mountDeferredValue(value);
      },
      useTransition: function () {
        currentHookNameInDev = 'useTransition';
        mountHookTypesDev();
        return mountTransition();
      },
      useMutableSource: function (source, getSnapshot, subscribe) {
        currentHookNameInDev = 'useMutableSource';
        mountHookTypesDev();
        return mountMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier: function () {
        currentHookNameInDev = 'useOpaqueIdentifier';
        mountHookTypesDev();
        return mountOpaqueIdentifier();
      },
      unstable_isNewReconciler: enableNewReconciler
    };

其中useEffect重點執(zhí)行mountEffect(create, deps)

function mountEffect(create, deps) {
    {
      // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
      if ('undefined' !== typeof jest) {
        warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber$1);
      }
    }
    return mountEffectImpl(Update | Passive, Passive$1, create, deps);
  }

mountEffectImpl

function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
  var hook = mountWorkInProgressHook();
    var nextDeps = deps === undefined ? null : deps;
    currentlyRenderingFiber$1.flags |= fiberFlags;
    hook.memoizedState = pushEffect(HasEffect | hookFlags, create, undefined, nextDeps);
}

hook對象數(shù)據(jù)結(jié)構(gòu)如下:

{
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null
};

問題來了:

  • currentlyRenderingFiber是什么?和workInProgressFiber有什么關系?
  • 多個函數(shù)組件和單個函數(shù)組件中多個Hook是如何關聯(lián)起來的?
  • 整個hook和fiber怎么關聯(lián)起來的?
  • 完整的hooks數(shù)據(jù)結(jié)構(gòu)又是什么?

currentlyRenderingFiber是當前正在rendering階段的fiber對象,早在renderHook初始化階段賦值了workInProgressFiber。所以當前函數(shù)組件的flags在這里被改變了,即有副作用的函數(shù)flags = flags | Update | Passive。

根據(jù)二進制位運算,根函數(shù)組件庫flags = 518,當然這個數(shù)值也不是固定不變的,因為變化的beginWork階段初始flags值。是根據(jù)不同的effects會有不同的初始值。

pushEffect

function pushEffect(tag, create, destroy, deps) {
    var effect = {
      tag: tag,
      create: create,
      destroy: destroy,
      deps: deps,
      // Circular
      next: null
    };
    var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
    if (componentUpdateQueue === null) {
      componentUpdateQueue = createFunctionComponentUpdateQueue();
      currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
      componentUpdateQueue.lastEffect = effect.next = effect;
    } else {
      var lastEffect = componentUpdateQueue.lastEffect;
      if (lastEffect === null) {
        componentUpdateQueue.lastEffect = effect.next = effect;
      } else {
        var firstEffect = lastEffect.next;
        lastEffect.next = effect;
        effect.next = firstEffect;
        componentUpdateQueue.lastEffect = effect;
      }
    }
    return effect;
  }

對于tag的值,是HasEffect和Passive按位運算或的結(jié)果,實際上固定是5。

需要注意的是,函數(shù)組件的updateQueue和rootFiber的不一樣,以及普通節(jié)點的數(shù)據(jù)結(jié)構(gòu)和作用也都不一樣。 函數(shù)組件的updateQueue關聯(lián)的是effect。這和render初始化階段rootFiber有巨大的差異。

上面的代碼很簡單,每個函數(shù)組件如果存在多個effect,那么會將這些effect順序關聯(lián)起來,這個函數(shù)組件的fiberr.updateQueue對應lastEffect,next即下一個effect,直到最后形成一個首尾相連的環(huán)狀鏈表結(jié)構(gòu)。

為什么是環(huán)狀?這個待到后續(xù)調(diào)度階段再解釋。

再思考一個問題:這里只是解決了單個組件內(nèi)的effect構(gòu)建,那么整個fiber鏈表里effect構(gòu)建是怎么樣的?執(zhí)行的順序又是什么?

四. rootFiber-Effect

在completedWork的最后,根據(jù)深度優(yōu)先遍歷算法,將每個節(jié)點的firstEffect層層往上傳遞,一直到rootFiber。而lastEffect也是層層往上判斷,直到上層最后一個effect,做為rootFiber的lastEffect。

每個fiber effect通過nextEffect鏈接起來,而fiber內(nèi)部通過updateQueue鏈接自身的effect環(huán)狀鏈表。

至此,completeWork階段就完成了,rootFiber以及各fiber節(jié)點大部分屬性都構(gòu)建完成了。

下一章,將進入commit階段,更多關于React Fiber構(gòu)建completeWork的資料請關注腳本之家其它相關文章!

以上就是React Fiber構(gòu)建completeWork源碼解析的詳細內(nèi)容,更多關于React Fiber構(gòu)建completeWork的資料請關注腳本之家其它相關文章!

相關文章

  • React使用Mobx6.x共享狀態(tài)問題

    React使用Mobx6.x共享狀態(tài)問題

    這篇文章主要介紹了React使用Mobx6.x共享狀態(tài)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • React之虛擬DOM的實現(xiàn)原理

    React之虛擬DOM的實現(xiàn)原理

    這篇文章主要介紹了React之虛擬DOM的實現(xiàn)原理分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • React+Mobx基本使用、模塊化操作

    React+Mobx基本使用、模塊化操作

    React 和 MobX 是一對強力組合,React 通過提供機制把應用狀態(tài)轉(zhuǎn)換為可渲染組件樹并對其進行渲染,而MobX提供機制來存儲和更新應用狀態(tài)供 React 使用,這篇文章主要介紹了React+Mobx基本使用、模塊化,需要的朋友可以參考下
    2022-09-09
  • react+typescript中使用echarts的實現(xiàn)步驟

    react+typescript中使用echarts的實現(xiàn)步驟

    本文主要介紹了react+typescript中使用echarts的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • 前端面試題必會之前端react面試題

    前端面試題必會之前端react面試題

    在前端面試過程中經(jīng)常會問到一些面試題,今天小編抽空給大家講解前端面試題之必會react面試題,需要的朋友可以參考下
    2023-03-03
  • 在React中編寫class樣式的方法總結(jié)

    在React中編寫class樣式的方法總結(jié)

    在TypeScript (TSX) 中編寫 CSS 樣式類有幾種方法,包括使用純 CSS、CSS Modules、Styled Components 等,本文給大家介紹了幾種常見方法的示例,通過代碼示例講解的非常詳細,需要的朋友可以參考下
    2024-07-07
  • react 組件表格固定底部的實現(xiàn)代碼

    react 組件表格固定底部的實現(xiàn)代碼

    在React中,要實現(xiàn)一個組件表格并且固定底部,可以使用CSS的固定定位或絕對定位來實現(xiàn),下面通過示例代碼給大家分享react 組件表格固定底部的實現(xiàn)代碼,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • 基于PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效

    基于PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效

    PixiJS是一個開源的基于web的渲染系統(tǒng),為游戲、數(shù)據(jù)可視化和其他圖形密集型項目提供了極快的性能,這篇文章主要介紹了用PixiJS實現(xiàn)react圖標旋轉(zhuǎn)動效,需要的朋友可以參考下
    2022-05-05
  • React數(shù)據(jù)傳遞的六種方式詳解

    React數(shù)據(jù)傳遞的六種方式詳解

    在 React 應用程序中,數(shù)據(jù)傳遞是非常重要的,它允許我們在組件之間共享信息和狀態(tài),從而構(gòu)建出復雜的用戶界面,本文將深入探討React數(shù)據(jù)傳遞的六種方式,需要的朋友可以參考下
    2025-08-08
  • React實現(xiàn)一個倒計時hook組件實戰(zhàn)示例

    React實現(xiàn)一個倒計時hook組件實戰(zhàn)示例

    這篇文章主要為大家介紹了React實現(xiàn)一個倒計時hook組件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02

最新評論

漳州市| 泗阳县| 元氏县| 黄冈市| 清原| 寻乌县| 巴中市| 泸溪县| 修武县| 黄龙县| 读书| 安宁市| 炎陵县| 江津市| 湖北省| 宜良县| 政和县| 民和| 海原县| 乡宁县| 南溪县| 建宁县| 阿合奇县| 从江县| 星子县| 玛沁县| 麻栗坡县| 文登市| 双辽市| 拉孜县| 大安市| 三门峡市| 科技| 汶上县| 鹤庆县| 阿拉善盟| 虞城县| 大安市| 峡江县| 民和| 当涂县|