React架构

React15的架构采用
Reconciler(协调器)- 决定渲染什么组件 (reconcile diff算法) -> 得到需要渲染的组件 -> 交给渲染器
Renderer(渲染器) - 将组件渲染到视图中

Scheduler(调度器)
Reconciler(协调器)
Renderer(渲染器)

  1. yarn build react/index,react-dom/index,scheduler --type=NODE
  2. yarn link

setState

  1. 在事件处理函数中或生命周期函数中批量更新的
  2. 在其他地方都是直接同步更新的,比如setTimeout

    commit 阶段

    before mutation
    mutation
    layout
    ReactFiberWookLoop.new.js ```javascript function commitRoot(root) { const renderPriorityLevel = getCurrentPriorityLevel(); // 调度的优先级 及调度的函数 runWithPriority( ImmediateSchedulerPriority, commitRootImpl.bind(null, root, renderPriorityLevel), ); return null; }
  1. <a name="keSvy"></a>
  2. # 源码
  3. <a name="RvemX"></a>
  4. #### ReactElement.js
  5. ```javascript
  6. const ReactElement = function(type, key, ref, self, source, owner, props) {
  7. const element = {
  8. // This tag allows us to uniquely identify this as a React Element
  9. $$typeof: REACT_ELEMENT_TYPE,
  10. // Built-in properties that belong on the element
  11. type: type,
  12. key: key,
  13. ref: ref,
  14. props: props,
  15. // Record the component responsible for creating this element.
  16. _owner: owner,
  17. };
  18. return element;
  19. };

createElement
/**
 * Create and return a new ReactElement of the given type.
 * See https://reactjs.org/docs/react-api.html#createelement
 */
export function createElement(type, config, children) {
  let propName;

  // Reserved names are extracted
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    if (hasValidKey(config)) {
      key = '' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  // Resolve default props
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

Fiber 类型

// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
export type Fiber = {|
  tag: WorkTag,
  key: null | string,
  elementType: any,
  type: any,
  stateNode: any,
  return: Fiber | null,
  child: Fiber | null,
  sibling: Fiber | null,
  index: number,
  ref:
    | null
    | (((handle: mixed) => void) & {_stringRef: ?string, ...})
    | RefObject,

  pendingProps: any, // This type will be more specific once we overload the tag.
  memoizedProps: any, // The props used to create the output.
  updateQueue: mixed,

  memoizedState: any,
  dependencies: Dependencies | null,
  mode: TypeOfMode,

  // Effect
  flags: Flags,
  subtreeFlags: Flags,
  deletions: Array<Fiber> | null,

  // Singly linked list fast path to the next fiber with side-effects.
  nextEffect: Fiber | null,

  // The first and last fiber with side-effect within this subtree. This allows
  // us to reuse a slice of the linked list when we reuse the work done within
  // this fiber.
  firstEffect: Fiber | null,
  lastEffect: Fiber | null,

  lanes: Lanes,
  childLanes: Lanes,

  // This is a pooled version of a Fiber. Every fiber that gets updated will
  // eventually have a pair. There are cases when we can clean up pairs to save
  // memory if we need to.
  alternate: Fiber | null,

  // Time spent rendering this Fiber and its descendants for the current update.
  // This tells us how well the tree makes use of sCU for memoization.
  // It is reset to 0 each time we render and only updated when we don't bailout.
  // This field is only set when the enableProfilerTimer flag is enabled.
  actualDuration?: number,

  // If the Fiber is currently active in the "render" phase,
  // This marks the time at which the work began.
  // This field is only set when the enableProfilerTimer flag is enabled.
  actualStartTime?: number,

  // Duration of the most recent render time for this Fiber.
  // This value is not updated when we bailout for memoization purposes.
  // This field is only set when the enableProfilerTimer flag is enabled.
  selfBaseDuration?: number,

  // Sum of base times for all descendants of this Fiber.
  // This value bubbles up during the "complete" phase.
  // This field is only set when the enableProfilerTimer flag is enabled.
  treeBaseDuration?: number,

  // Conceptual aliases
  // workInProgress : Fiber ->  alternate The alternate used for reuse happens
  // to be the same as work in progress.
  // __DEV__ only
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
  _debugNeedsRemount?: boolean,

  // Used to verify that the order of hooks does not change between renders.
  _debugHookTypes?: Array<HookType> | null,
|};

Fiber 更新

1_ad-k5hYKQnRQJF8tv8BIqg.png

  • 每个帧的开头包括样式计算、布局和绘制
  • JavaScript执行引擎和页面渲染引擎在同一个渲染线程,GUI渲染和JavaScript渲染是互斥的
  • 如果某个任务执行时间过长,浏览器会推迟渲染 (卡顿)

    fiber执行阶段

  • 每次渲染有两个阶段: Reconciliation(协调\render阶段)和Commit提交阶段

  • 协调阶段: 可以认为是Diff阶段,这个阶段可以被中断,这个阶段会找出所有的节点变更、例如节点新增、删除、属性变更等等,这些变更React称为副作用(Effect)
  • 提交阶段: 将上一个阶段计算出来的需要处理的副作用(Effects)一次性执行了,这个阶段必须同步执行、不能被打断

    实践问题

    useState 从props里设置初始值问题

    // useState 在设置的时候,只执行一次回调
    // useEffect deps[] 时也是只执行一次
    
    https://www.debuggr.io/react-update-unmounted-component/
    useEffect 完整指南

参考链接

Build your own React
requestAnimationFrame Scheduling For Nerds