react实现分析

本文最后更新于:2022年7月12日 下午

React

⭐️ 时间切片

  • 主流显示器的刷新率是 60Hz,浏览器渲染一帧需要执行执行js脚本,Layout重排,Paint重绘
  • 如果 js 脚本的执行时间超过 16.6ms,就会造成浏览器这次刷新没有时间去绘制,造成卡顿
  • React 在每一帧中预留了时间给 js 任务执行(5ms),并将大的 js 任务拆分到每一帧中

React 架构

旧架构

  • Reconciler协调器查找组件的更新
  • Renderer渲染器将更新内容渲染到页面上
  • 交替执行
  • Reconciler使用递归来更新组件,一旦组件嵌套过深其实也会超过 16.6ms 造成卡顿

新架构

  • Scheduler当目前帧浏览器有多余时间可用时调度任务进入Reconciler
  • Reconciler为组件打上删除,更新,插入的标记
  • Reconciler完成全部工作后交给Renderer更新 DOM
  • 组件的更新(打标记)会由于任务优先级或者浏览器剩余时间的变化随时中断

Fiber 架构

一套应用程序的状态可中断可恢复的更新机制

⭐️ 双缓存

  • 在内存中构建更新的状态并直接替换
  • Fiber 树有两颗,前屏幕上显示内容对应的 Fiber 树称为current Fiber树,正在内存中构建的 Fiber 树称为workInProgress Fiber

每一个 Fiber 节点包含:

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance
  this.tag = tag; // class/func component
  this.key = key; // ⭐️key
  this.elementType = null;
  this.type = null;
  this.stateNode = null; // DOM

  // 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.dependencies = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect; // effect标记
  this.subtreeTag = NoSubtreeEffect;
  this.deletions = null;
  this.nextEffect = null;

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

  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  this.alternate = null; // 上一次更新的fiberNode
}
  • 组件类型
  • ⭐️key
  • DOM节点
  • 指向其他 Fiber 节点的指针
  • 本次更新的相关信息
  • 调度优先级信息

执行阶段

render 阶段

  • rootFiber开始向下深度遍历,调用 fiber 节点的beginWork方法,创建新的子 fiber 节点,一直到叶子节点

  • 某个 fiber 节点执行完completeWork后,会访问其兄弟节点让他兄弟执行beginWork

  • 没有兄弟节则return到父节点

  • 一直 return 到rootFiberrender结束

beginWork

⭐️[[diff算法]]

创建子 fiber 节点,可能复用上一次更新的子 fiber 节点,或者新建新 fiber 节点,并在上面打上标记

  • ⚠️renderWithHooks

completeWork

区分 mount/update,生成新的 DOM 节点,并处理propsreturnrootFiber时就已拥有了一颗完整的 DOM 树

commit 阶段

执行effectList,用来保存 fiber 节点需要执行副作用的单向链表,执行相应的 DOM 操作。

commitBeforeMutationEffects

  • getSnapshotBeforeUpdate
  • flushPassiveEffects

commitMutationEffects

  • 解绑ref
  • ⚠️ 同步执行useLayoutEffect的销毁函数
  • 根据不同的标记执行DOM操作
  • componentWillUnmount

switch root.current

切换 current fiber 树

commitLayoutEffects

  • 赋值新的ref
  • ⚠️ 同步执行useLayoutEffect的回调
  • ⚠️ 异步执行useEffect的销毁和回调
  • componentDidMount
  • componentDidUpdate
  • 执行 render 函数的回调

状态更新

  • ReactDOM.render
  • this.setState
  • this.forceUpdate
  • useState
  • useReducer

每次触发状态更新都会新建Update对象

组件中多次触发的update也会形成一条链表

const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState, // 之前的state
    firstBaseUpdate: null, 
    lastBaseUpdate: null,
    // 👆本次更新前的update链表
    shared: {
      pending: null,
    },
    effects: null,
  };

更新的优先级

  • 生命周期钩子(同步)
  • 用户输入(同步)
  • UI交互(高优先级)
  • 其他
  • 一次render中的部分低优先级update可能被跳过/插队

react实现分析
http://yoursite.com/2022/05/10/[源码]react/
作者
tatekii
发布于
2022年5月10日
许可协议