码迷,mamicode.com
首页 > 其他好文 > 详细

React 16 版本中初始渲染的流程

时间:2021-06-02 19:00:59      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:ras   empty   oal   with   hand   poi   tables   rop   calling   

React 源码分析~初次渲染

一、下载源码到本地

二、Render 为 入口

1. 导出 render

packages/react-dom/src/client/ReactDOM.js

import {
  findDOMNode,
  render,
  hydrate,
  unstable_renderSubtreeIntoContainer,
  unmountComponentAtNode,
} from ‘./ReactDOMLegacy‘;

export {
  createPortal,
  batchedUpdates as unstable_batchedUpdates,
  flushSync,
  Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
  ReactVersion as version,
  // Disabled behind disableLegacyReactDOMAPIs
  findDOMNode,
  hydrate,
  render, // 此处导出
  unmountComponentAtNode,
  // exposeConcurrentModeAPIs
  createRoot,
  flushControlled as unstable_flushControlled,
  scheduleHydration as unstable_scheduleHydration,
  // Disabled behind disableUnstableRenderSubtreeIntoContainer
  renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer,
  // enableCreateEventHandleAPI
  createEventHandle as unstable_createEventHandle,
  // TODO: Remove this once callers migrate to alternatives.
  // This should only be used by React internals.
  runWithPriority as unstable_runWithPriority,
};

  

2. 当初始化渲染时,删除 container 内其他额外的元素,并在 root 上挂载 _internalRoot

packages/react-dom/src/client/ReactDOMLegacy.js

import {
  findHostInstanceWithNoPortals,
  updateContainer,
  unbatchedUpdates,
  getPublicRootInstance,
  findHostInstance,
  findHostInstanceWithWarning,
} from ‘react-reconciler/src/ReactFiberReconciler‘;

export function render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) {
  invariant(
    isValidContainer(container),
    ‘Target container is not a DOM element.‘,
  );
  if (__DEV__) {
    const isModernRoot =
      isContainerMarkedAsRoot(container) &&
      container._reactRootContainer === undefined;
    if (isModernRoot) {
      console.error(
        ‘You are calling ReactDOM.render() on a container that was previously ‘ +
        ‘passed to ReactDOM.createRoot(). This is not supported. ‘ +
        ‘Did you mean to call root.render(element)?‘,
      );
    }
  }
  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}


function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: Container,
  forceHydrate: boolean,
  callback: ?Function,
) {
  if (__DEV__) {
    topLevelUpdateWarnings(container);
    warnOnInvalidCallback(callback === undefined ? null : callback, ‘render‘);
  }
  
  let root = container._reactRootContainer;
  let fiberRoot: FiberRoot;
  if (!root) {
    // Initial mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
      container,
      forceHydrate,
    );
    
  }

}


/**
 * 当初始化渲染时,删除 container 内其他额外的元素
 */
function legacyCreateRootFromDOMContainer(
  container: Container,
  forceHydrate: boolean,
): RootType {
  // First clear any existing content.
  if (!forceHydrate) {
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      container.removeChild(rootSibling);
    }
  }

  return createLegacyRoot(
    container,
    forceHydrate
      ? {
        hydrate: true,
      }
      : undefined,
  );
}

 

3. FirberRoot 对象,RootFirber 对象创建的过程

packages/react-dom/src/client/ReactDOMRoot.js

export function createLegacyRoot(
  container: Container,
  options?: RootOptions,
): RootType {
  return new ReactDOMLegacyRoot(container, options);
}

function ReactDOMLegacyRoot(container: Container, options: void | RootOptions) {
  this._internalRoot = createRootImpl(container, LegacyRoot, options);
}

/**
 * 创建 FirberRoot 对象
 * 并在 FirberRoot 上挂载 rootFirber
 * 并将其返回到 _internalRoot 对象上,此时 _internalRoot = FirberRoot
 */
function createRootImpl(
  container: Container,
  tag: RootTag,
  options: void | RootOptions,
) {
  // Tag is either LegacyRoot or Concurrent Root
  const hydrate = options != null && options.hydrate === true;
  const hydrationCallbacks =
    (options != null && options.hydrationOptions) || null;
  const mutableSources =
    (options != null &&
      options.hydrationOptions != null &&
      options.hydrationOptions.mutableSources) ||
    null;
  const isStrictMode = options != null && options.unstable_strictMode === true;

  let concurrentUpdatesByDefaultOverride = null;
  if (allowConcurrentByDefault) {
    concurrentUpdatesByDefaultOverride =
      options != null && options.unstable_concurrentUpdatesByDefault != null
        ? options.unstable_concurrentUpdatesByDefault
        : null;
  }

  // 将 id="root" 转化为 Firber 对象
  const root = createContainer(
    container,
    tag,
    hydrate,
    hydrationCallbacks,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
  );

  // 重新 node[__reactContainer] = #idrootFirber
  markContainerAsRoot(root.current, container);

  const rootContainerElement =
    container.nodeType === COMMENT_NODE ? container.parentNode : container;
  listenToAllSupportedEvents(rootContainerElement);

  if (mutableSources) {
    for (let i = 0; i < mutableSources.length; i++) {
      const mutableSource = mutableSources[i];
      registerMutableSourceForHydration(root, mutableSource);
    }
  }

  return root;
}

  

4. 选择 创建Firber 的方式?

packages/react-reconciler/src/ReactFiberReconciler.js

 
    export const enableNewReconciler = false;

import { createContainer as createContainer_old, getCurrentUpdatePriority as getCurrentUpdatePriority_old, } from ‘./ReactFiberReconciler.old‘; import { createContainer as createContainer_new, } from ‘./ReactFiberReconciler.new‘; export const createContainer = enableNewReconciler ? createContainer_new : createContainer_old;

  

5. createContainer_old 创建 Firber 的方式( 此处为重点的重点,包括React的重点流程在此处,可多次反复查看代码 )

import { createFiberRoot } from ‘./ReactFiberRoot.old‘;

export function createContainer(
  containerInfo: Container,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
  isStrictMode: boolean,
  concurrentUpdatesByDefaultOverride: null | boolean,
): OpaqueRoot {
  return createFiberRoot(
    containerInfo,
    tag,
    hydrate,
    hydrationCallbacks,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
  );
}

/**
 * 1. 创建 FiberRoot
 * 2. 创建 RootFiber
 * 3. 在 Fiber.current 上挂载 RootFiber
 * 4. 在 RootFiber.stateNode 挂载 FiberRoot
 * 5. 初始化队列:  在 RootFiber 上挂载 updateQueue
 */
export function createFiberRoot(
  containerInfo: any,
  tag: RootTag,
  hydrate: boolean,
  hydrationCallbacks: null | SuspenseHydrationCallbacks,
  isStrictMode: boolean,
  concurrentUpdatesByDefaultOverride: null | boolean,
): FiberRoot {
  const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
  if (enableSuspenseCallback) {
    root.hydrationCallbacks = hydrationCallbacks;
  }

  // Cyclic construction. This cheats the type system right now because
  // stateNode is any.
  // 创建 RootFiber
  const uninitializedFiber = createHostRootFiber(
    tag,
    isStrictMode,
    concurrentUpdatesByDefaultOverride,
  );
  // 在 Fiber.current 上挂载 RootFiber
  root.current = uninitializedFiber;
  // 在 RootFiber.stateNode 挂载 FiberRoot
  uninitializedFiber.stateNode = root;

  if (enableCache) {
    const initialCache = new Map();
    root.pooledCache = initialCache;
    const initialState = {
      element: null,
      cache: initialCache,
    };
    uninitializedFiber.memoizedState = initialState;
  } else {
    const initialState = {
      element: null,
    };
    uninitializedFiber.memoizedState = initialState;
  }
  // 初始化更新队列
  initializeUpdateQueue(uninitializedFiber);

  return root;
}

  

6. 初次创建 RootFiber(此处是)

/**
 * 创建 RootFiber,并返回 Firber
 * tag = 0
  */
export function createHostRootFiber(
  tag: RootTag,
  isStrictMode: boolean,
  concurrentUpdatesByDefaultOverride: null | boolean,
): Fiber {
  let mode;
  if (tag === ConcurrentRoot) {
    mode = ConcurrentMode;
    if (isStrictMode === true) {
      mode |= StrictLegacyMode;

      if (enableStrictEffects) {
        mode |= StrictEffectsMode;
      }
    } else if (enableStrictEffects && createRootStrictEffectsByDefault) {
      mode |= StrictLegacyMode | StrictEffectsMode;
    }
    if (
      // We only use this flag for our repo tests to check both behaviors.
      // TODO: Flip this flag and rename it something like "forceConcurrentByDefaultForTesting"
      !enableSyncDefaultUpdates ||
      // Only for internal experiments.
      (allowConcurrentByDefault && concurrentUpdatesByDefaultOverride)
    ) {
      mode |= ConcurrentUpdatesByDefaultMode;
    }
  } else {
    mode = NoMode;
  }

  if (enableProfilerTimer && isDevToolsPresent) {
    // Always collect profile timings when DevTools are present.
    // This enables DevTools to start capturing timing at any point–
    // Without some nodes in the tree having empty base times.
    mode |= ProfileMode;
  }

  return createFiber(HostRoot, null, null, mode);
}


// 将 RootFiber 的格式 以实例的形式返回
const createFiber = function (
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
): Fiber {
  // $FlowFixMe: the shapes are exact here but Flow doesn‘t like constructors
  return new FiberNode(tag, pendingProps, key, mode);
};

// RootFiber 的格式,
// 此处的 pendingProps = null
// mode = 0b000000
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance 实例
  // 判断是否是初次,0=初次,1=不初次
  this.tag = tag;
  // 唯一标识
  this.key = key;
  // dom 的标签
  this.elementType = null;
  // JSX的类型,Component? Function? String
  this.type = null;
  // dom 元素
  this.stateNode = null;

  // Fiber
  // 父级
  this.return = null;
  // 子级 Firber
  this.child = null;
  // 同级 Firber
  this.sibling = null;
  this.index = 0;

  // ref 挂载
  this.ref = null;

  // 暂定
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  // 队列
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.flags = NoFlags;
  this.subtreeFlags = NoFlags;
  this.deletions = null;

  this.lanes = NoLanes;
  this.childLanes = NoLanes;
  
  this.alternate = null;

  if (enableProfilerTimer) {
    // Note: The following is done to avoid a v8 performance cliff.
    //
    // Initializing the fields below to smis and later updating them with
    // double values will cause Fibers to end up having separate shapes.
    // This behavior/bug has something to do with Object.preventExtension().
    // Fortunately this only impacts DEV builds.
    // Unfortunately it makes React unusably slow for some applications.
    // To work around this, initialize the fields below with doubles.
    //
    // Learn more about this here:
    // https://github.com/facebook/react/issues/14365
    // https://bugs.chromium.org/p/v8/issues/detail?id=8538
    this.actualDuration = Number.NaN;
    this.actualStartTime = Number.NaN;
    this.selfBaseDuration = Number.NaN;
    this.treeBaseDuration = Number.NaN;

    // It‘s okay to replace the initial doubles with smis after initialization.
    // This won‘t trigger the performance cliff mentioned above,
    // and it simplifies other profiler code (including DevTools).
    this.actualDuration = 0;
    this.actualStartTime = -1;
    this.selfBaseDuration = 0;
    this.treeBaseDuration = 0;
  }

  if (__DEV__) {
    // This isn‘t directly used but is handy for debugging internals:
    this._debugID = debugCounter++;
    this._debugSource = null;
    this._debugOwner = null;
    this._debugNeedsRemount = false;
    this._debugHookTypes = null;
    if (!hasBadMapPolyfill && typeof Object.preventExtensions === ‘function‘) {
      Object.preventExtensions(this);
    }
  }
}

 

7. 初始化队列,并将其挂载到 RootFiber 上

export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    firstBaseUpdate: null,
    lastBaseUpdate: null,
    shared: {
      pending: null,
      interleaved: null,
      lanes: NoLanes,
    },
    effects: null, // 更新事件
  };
  // 挂载队列
  fiber.updateQueue = queue;
}

  

  

React 16 版本中初始渲染的流程

标签:ras   empty   oal   with   hand   poi   tables   rop   calling   

原文地址:https://www.cnblogs.com/gqx-html/p/14832327.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!