Foveluy / Luy

a React-like framework
Other
356 stars 31 forks source link

React 16 架构学习资料 #10

Open Foveluy opened 6 years ago

Foveluy commented 6 years ago
Foveluy commented 6 years ago

fiber tag

export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;

export const IndeterminateComponent = 0; // 尚不知是类组件还是函数式组件
export const FunctionalComponent = 1; // 函数式组件
export const ClassComponent = 2; // Class类组件
export const HostRoot = 3; // 组件树根组件,可以嵌套
export const HostPortal = 4; // 子树. Could be an entry point to a different renderer.
export const HostComponent = 5; // 标准组件,如地div, span等
export const HostText = 6; // 文本
export const CallComponent = 7; // 组件调用
export const CallHandlerPhase = 8; // 调用组件方法
export const ReturnComponent = 9; // placeholder(占位符)
export const Fragment = 10; // 片段
Foveluy commented 6 years ago
Foveluy commented 6 years ago
// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.

var debugCounter = void 0;

{
  debugCounter = 1;
}

function FiberNode(tag, pendingProps, key, mode) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this['return'] = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;

  this.alternate = null;

  {
    this._debugID = debugCounter++;
    this._debugSource = null;
    this._debugOwner = null;
    this._debugIsCurrentlyTiming = false;
    if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
      Object.preventExtensions(this);
    }
  }
}
Foveluy commented 6 years ago
render
|
v
legacyCreateRootFromDOMContainer
//这个函数会给 root container 创建一个 Root.fiber 和是一个特殊的对象
function legacyCreateRootFromDOMContainer(container, forceHydrate) {
  var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  // First clear any existing content.
  if (!shouldHydrate) {
    var warned = false;
    var rootSibling = void 0;
    while (rootSibling = container.lastChild) {
      {
        if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
          warned = true;
          warning(false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.');
        }
      }
      container.removeChild(rootSibling);
    }
  }
  {
    if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
      warnedAboutHydrateAPI = true;
      lowPriorityWarning$1(false, 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v17. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.');
    }
  }
  // Legacy roots are not async by default.
  var isAsync = false;
  return new ReactRoot(container, isAsync, shouldHydrate);
}
// 那么 fiber root 的结构是
function createFiberRoot(containerInfo, isAsync, hydrate) {
  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  var uninitializedFiber = createHostRootFiber(isAsync);
  var root = {
    current: uninitializedFiber,
    containerInfo: containerInfo,
    pendingChildren: null,
    pendingCommitExpirationTime: NoWork,
    finishedWork: null,
    context: null,
    pendingContext: null,
    hydrate: hydrate,
    remainingExpirationTime: NoWork,
    firstBatch: null,
    nextScheduledRoot: null
  };
  uninitializedFiber.stateNode = root;
  return root;
}

|
|
v

//首次渲染不会执行批量更新
DOMRenderer.unbatchedUpdates
|
v
updateContainer
|
v
//获取context
  var context = getContextForSubtree(parentComponent);
    if (container.context === null) {
      container.context = context;
    } else {
      container.pendingContext = context;
    }
|
v
scheduleRootUpdate()
//伴随着以下数据结构
 var update = {
      expirationTime: expirationTime,
      partialState: { element: element },
      callback: callback,
      isReplace: false,
      isForced: false,
      capturedValue: null,
      next: null
    };
|
v
insertUpdateIntoFiber()
 // 这个函数做了一件事情
// setState 和 render 的时候,都会创建一个 update,这个方法就是把这个 update 放入队列中,这个队列是一个链表
|
v
//是执行虚拟DOM(fiber树)的更新
scheduleWorkImpl() --->scheduleWork是他的包囊方法,其实没啥用
|
v
// Add this root to the root schedule.
requestWork(root, expirationTime);
|
v
// 调用 performSyncWork()
 if (expirationTime === Sync) {
      performSyncWork();
    } else {
      scheduleCallbackWithExpiration(expirationTime);
}
|
v
// performWork(Sync, false, null);
performWork(Sync, false, null);
|
v
performWorkOnRoot()
|
v
//往下走到达 
renderRoot(root, expirationTime, isAsync)
//再往下就是一个关键的方法,这个方法中,我们通过现有的 Fiber 构建出 
// WorkInProgress tree,WorkInProgress tree 就是新的 Fiber
// 对其进行比较
function createWorkInProgress(current, pendingProps, expirationTime)
// This is used to create an alternate fiber to do work on.
|
v
workloop
|
|
v
performUnitOfWork(workInProgress)
|
|
v
//这个方法,返回下一个要处理的 Fiber
function beginWork(current, workInProgress, renderExpirationTime) 
// 这个函数是一个 switch tag.
  switch (workInProgress.tag) {
      case IndeterminateComponent:
        return mountIndeterminateComponent(current, workInProgress, renderExpirationTime);
      case FunctionalComponent:
        return updateFunctionalComponent(current, workInProgress);
      case ClassComponent:
        return updateClassComponent(current, workInProgress, renderExpirationTime);
      case HostRoot:
        return updateHostRoot(current, workInProgress, renderExpirationTime);
      case HostComponent:
        return updateHostComponent(current, workInProgress, renderExpirationTime);
      case HostText:
        return updateHostText(current, workInProgress);
      case CallHandlerPhase:
        // This is a restart. Reset the tag to the initial phase.
        workInProgress.tag = CallComponent;
      // Intentionally fall through since this is now the same.
      case CallComponent:
        return updateCallComponent(current, workInProgress, renderExpirationTime);
      case ReturnComponent:
        // A return component is just a placeholder, we can just run through the
        // next one immediately.
        return null;
      case HostPortal:
        return updatePortalComponent(current, workInProgress, renderExpirationTime);
      case ForwardRef:
        return updateForwardRef(current, workInProgress);
      case Fragment:
        return updateFragment(current, workInProgress);
      case Mode:
        return updateMode(current, workInProgress);
      case ContextProvider:
        return updateContextProvider(current, workInProgress, renderExpirationTime);
      case ContextConsumer:
        return updateContextConsumer(current, workInProgress, renderExpirationTime);
      default:
        invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.');
    }
  }
|
|
v
workInProgress.tag 是什么?
https://github.com/facebook/react/blob/master/packages/shared/ReactTypeOfWork.js
|
|
v
//核心方法
 function reconcileChildren(current, workInProgress, nextChildren)
|
|
v

// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
|
v
 // Commit the root.
root.finishedWork = null;
root.remainingExpirationTime = commitRoot(finishedWork);
|
v
//commitRoot() 巨复杂
|
v
commitLifeCycles()

//是一个大循环,拿到 effect list 进行循环

while (nextEffect !== null) {
      var effectTag = nextEffect.effectTag;

      if (effectTag & (Update | Callback)) {
        recordEffect();
        var current = nextEffect.alternate;
        commitLifeCycles(finishedRoot, current, nextEffect, currentTime, committedExpirationTime);
      }

      if (effectTag & ErrLog) {
        commitErrorLogging(nextEffect, onUncaughtError);
      }

      if (effectTag & Ref) {
        recordEffect();
        commitAttachRef(nextEffect);
      }

      var next = nextEffect.nextEffect;
      // Ensure that we clean these up so that we don't accidentally keep them.
      // I'm not actually sure this matters because we can't reset firstEffect
      // and lastEffect since they're on every node, not just the effectful
      // ones. So we have to clean everything as we reuse nodes anyway.
      nextEffect.nextEffect = null;
      // Ensure that we reset the effectTag here so that we can rely on effect
      // tags to reason about the current life-cycle.
      nextEffect = next;
    }
|
v