关于前端:深入理解React中fiber

35次阅读

共计 6220 个字符,预计需要花费 16 分钟才能阅读完成。

一、前言 Fiber 是对 React 外围算法的重写,Fiber 是 React 外部定义的一种数据结构,将更新渲染耗时长的大工作,分为许多的小片。Fiber 节点保留啦组件须要更新的状态和副作用,一个 Fiber 代表一个工作单元。二、Fiber 在 React 做了什么在 react 中,次要做了上面这些操作:为每个减少了优先级,优先级高的工作能够中断低优先级的工作。而后再从新,留神是从新执行优先级低的工作减少了异步工作,调用 requestIdleCallback api,浏览器闲暇的时候执行 dom diff 树变成了链表,一个 dom 对应两个 fiber(一个链表),对应两个队列,这都是为找到被中断的工作,从新执行 Fiber 中的属性 type Fiber = {// 用于标记 fiber 的 WorkTag 类型,次要示意以后 fiber 代表的组件类型如 FunctionComponent、ClassComponent 等  tag: WorkTag,  // ReactElement 外面的 key  key: null | string,  // ReactElement.type,调用 createElement 的第一个参数  elementType: any,  // The resolved function/class/ associated with this fiber.  // 示意以后代表的节点类型  type: any,  // 示意以后 FiberNode 对应的 element 组件实例  stateNode: any,  // 指向他在 Fiber 节点树中的 parent,用来在解决完这个节点之后向上返回  return: Fiber | null,  // 指向本人的第一个子节点  child: Fiber | null,  // 指向本人的兄弟构造,兄弟节点的 return 指向同一个父节点  sibling: Fiber | null,  index: number,  ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,  // 以后处理过程中的组件 props 对象  pendingProps: any,  // 上一次渲染实现之后的 props  memoizedProps: any,  // 该 Fiber 对应的组件产生的 Update 会寄存在这个队列外面  updateQueue: UpdateQueue<any> | null,  // 上一次渲染的时候的 state  memoizedState: any,  // 一个列表,寄存这个 Fiber 依赖的 context  firstContextDependency: ContextDependency<mixed> | null,  mode: TypeOfMode,  // Effect  // 用来记录 Side Effect  effectTag: SideEffectTag,  // 单链表用来疾速查找下一个 side effect  nextEffect: Fiber | null,  // 子树中第一个 side effect  firstEffect: Fiber | null,  // 子树中最初一个 side effect  lastEffect: Fiber | null,  // 代表工作在将来的哪个工夫点应该被实现,之后版本改名为 lanes  expirationTime: ExpirationTime,  // 疾速确定子树中是否有不在期待的变动  childExpirationTime: ExpirationTime,  // fiber 的版本池,即记录 fiber 更新过程,便于复原  alternate: Fiber | null,} 三、从架构的角度了解 Fiber 增量渲染把一个渲染工作分解成多个,而后扩散在多个帧。实现工作能够中断、能够复原,并且能够给不同的工作赋予不同的优先级,最终实现更加丝滑的用户体验。React16 之前,React 的渲染和更新依赖分为 Reconciler->Render,Reconciler 比照新旧虚构 DOM(Document Object Model)的变动,Render 将变动利用到视图。React16 加多了一个 Scheduler,用来调度更新的优先级。更新的流程变成:每一个更新的工作都被赋予一个优先级,Scheduler 把优先级高的先 Reconciler,如果有一个优先级比之前的工作更高的,之前的工作会中断,执行完后,新一轮调度之前被中断的工作会从新 Reconciler,持续渲染。四、Fiber 的 concurrent 模式在 React 中,异步渲染中“工夫切片”、“优先级”是 Scheduler 的外围能力,Scheduler 在源码目录中与 react-dom 是同级的。

咱们都晓得浏览器的刷新频率是 60Hz,每 16.6ms 会刷新一次,没开启 Concurrent 模式,能够看到浏览器的 Task 中灰色的那长条不可中断工作,调用了 createRoot 后,那条大工作被切割成许个个小工作。切割后的小工作工作量加起来跟之前那条大工作是一样的,这就是“工夫切片”成果。如何实现工夫切片在源代码中,搜寻 workLoopSync 函数就能够看到。

  function wrokLoopSync () {      while (workInProgress !== null) {performUnitOfWork(workInProgress)      }    }同步渲染 wrokLoopSync 中 while 循环中触发下一个同步 performUnitOfWork。

异步渲染 workLoopConcurrent 中 while 循环触发也是 performUnitOfWork,只不过多了一个 shouldYield,这个是用来解决让出主过程的。初略了解:

React 依据浏览器的帧率计算出工夫切片大小,联合以后工夫计算每一个切片的到期工夫,workLoopConcurrent 中每一个循环都会判断是否到期,让出主线程。如何实现优先级调度 Scheduler 中的 unstable_scheduleCallback 函数是一个外围办法,解决工作的优先级执行不同的调度逻辑。在源码门路~/packages/scheduler/src/forks/Scheduler.js 中能够看到这个办法。function unstable_scheduleCallback(priorityLevel: PriorityLevel,  callback: Callback,  options?: {delay: number},): Task {//  获取以后工夫  var currentTime = getCurrentTime(); // 工作的预期开始工夫  var startTime;  // 解决 options 的入参  if (typeof options === ‘object’ && options !== null) {var delay = options.delay;    // 如果定义了延迟时间,在加上这个延迟时间    if (typeof delay === ‘number’ && delay > 0) {startTime = currentTime + delay;} else {startTime = currentTime;}  } else {startTime = currentTime;} // 解决 exoirationTime 的计算根据  var timeout;  // 依据 priorityLevel 给 timeout 赋值  switch (priorityLevel) {case ImmediatePriority:      timeout = IMMEDIATE_PRIORITY_TIMEOUT;      break;    case UserBlockingPriority:      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;      break;    case IdlePriority:      timeout = IDLE_PRIORITY_TIMEOUT;      break;    case LowPriority:      timeout = LOW_PRIORITY_TIMEOUT;      break;    case NormalPriority:    default:      timeout = NORMAL_PRIORITY_TIMEOUT;      break;}  // 优先级越高,timeout 越小,expirationTime 越小  var expirationTime = startTime + timeout; // 创立工作对象  var newTask: Task = {id: taskIdCounter++,    callback,    priorityLevel,    startTime,    expirationTime,    sortIndex: -1,};  if (enableProfiling) {newTask.isQueued = false;} // 如果以后工夫小于开始工夫,阐明该工作能够提早(还没过期)if (startTime > currentTime) {// 提早工作    newTask.sortIndex = startTime;    push(timerQueue, newTask);    // 如果工作队列没有能够执行的工作,而且当前任务又是工作队列的第一个工作    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {// All tasks are delayed, and this is the task with the earliest delay.      if (isHostTimeoutScheduled) {// Cancel an existing timeout.        cancelHostTimeout();      } else {isHostTimeoutScheduled = true;}      // 派发一个延时工作,查看是否过期      requestHostTimeout(handleTimeout, startTime – currentTime);    }  } else {// 解决工作过期逻辑    newTask.sortIndex = expirationTime;    // 过期工作推入 taskQueue    push(taskQueue, newTask);    if (enableProfiling) {markTaskStart(newTask, currentTime);      newTask.isQueued = true;    }    // Schedule a host callback, if needed. If we’re already performing work,    // wait until the next time we yield.    if (!isHostCallbackScheduled && !isPerformingWork) {isHostCallbackScheduled = true;      // 执行 taskQueue 中的工作      requestHostCallback();    }  }  return newTask;}这个函数大略意思:创立 task,而后依据 startTime 工作的预期开始工夫把 task 推入 timerQueue 或者 taskQueue,最初依据 timerQueue、taskQueue 执行延时工作或者即时工作。从下面的函数能够看出几个要害信息:expirationTime 越小,工作优先级越高 timerQueue 是用来存储待执行的工作 taskQueue 是用开存储过期的工作五、Fiber 中的一些函数 createFibermount 过程中,创立了 rootFiber,是 react 利用的根 fiber。function createFiber(tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode,): Fiber {// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn’t like constructors  return new FiberNode(tag, pendingProps, key, mode);}Fiber 的深度遍历开始:Fiber 从最下面的 React 元素开始遍历,并为其创立一个 fiber 节点。子节点:而后,它转到子元素,为这个元素创立一个 fiber 节点。这样继续下去直到在没有孩子兄弟节点:当初,它查看是否有兄弟节点元素。如果有,它就遍历兄弟节点元素,而后再到兄弟姐妹的叶子元素。返回:如果没有兄弟节点,那么它就返回到父节点。createWorkInProgress 更新过程,创立 workInProgress fiber,对其标记副作用。current Fiber 中每个 fiber 节点通过 alternate 字段,指向 workInProgress Fiber 中对应的 fiber 节点。同样 workInProgress Fiber 中的 fiber 节点的 alternate 字段也会指向 current Fiber 中对应的 fiber 节点。源代码门路~/packages/react-reconciler/src/ReactFiber.js

window.requestIdleCallback()将在浏览器的闲暇时段内调用的函数排队。办法提供 deadline,即工作执行限度工夫,以切分工作,防止长时间执行,阻塞 UI 渲染而导致掉帧;【安顿低优先级或非必要的函数在帧完结时的闲暇工夫被调用】requestAnimationFrame

安顿高优先级的函数在下一个动画帧之前被调用六、最初 React Fiber scheduler 将工作分为多个工作单元。它设置每个工作的优先级,并使暂停、重用和停止工作单元。

正文完
 0