博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Fiber源码分析 第二篇(同步模式)
阅读量:6625 次
发布时间:2019-06-25

本文共 10267 字,大约阅读时间需要 34 分钟。

系列文章

前言

React Fiber是React在V16版本中的大更新,利用了闲余时间看了一些源码,做个小记录~ 如果有错误,请轻喷

流程图

流程图1

流程图2

源码分析

1.scheduleRootUpdate这个函数主要执行了两个操作 1个是创建更新createUpdate并放到更新队列enqueueUpdate, 1个是执行sheculeWork函数

function scheduleRootUpdate(current$$1, element, expirationTime, callback) {var update = createUpdate(expirationTime);  update.payload = { element: element };  callback = callback === undefined ? null : callback;  if (callback !== null) {    update.callback = callback;  }  enqueueUpdate(current$$1, update);  scheduleWork(current$$1, expirationTime);  return expirationTime;}复制代码

2.先从createUpdate函数分析, 他直接返回了一个包含了更新信息的对象

function createUpdate(expirationTime) {  return {    // 优先级    expirationTime: expirationTime,    // 更新类型    tag: UpdateState,    // 更新的对象    payload: null,    callback: null,    // 指向下一个更新    next: null,    // 指向下一个更新effect    nextEffect: null  };}复制代码

3.接着更新payload和callback属性, payload即为更新的对象, 然后执行enqueuUpdateenqueueUpdate相对比较容易理解, 不过里面有一注释挺重要

Both queues are non-empty. The last update is the same in both lists, because of structural sharing. So, only append to one of the lists 意思是alternate的updateQueue和fiber的updateQueue是同一个对象引用,这里会在createWorkInProcess提到

往下走就是重要的scheduleWork, 它是render阶段真正的开始

function scheduleWork(fiber, expirationTime) {  // 更新优先级  var root = scheduleWorkToRoot(fiber, expirationTime);  ...if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime < nextRenderExpirationTime) {    // This is an interruption. (Used for performance tracking.) 如果这是一个打断原有更新的任务, 先把现有任务记录    interruptedBy = fiber;    resetStack();  }  // 设置下一个操作时间nextExpirationTimeToWorkOn  markPendingPriorityLevel(root, expirationTime);  if (  // If we're in the render phase, we don't need to schedule this root  // for an update, because we'll do it before we exit...  !isWorking || isCommitting$1 ||  // ...unless this is a different root than the one we're rendering.  nextRoot !== root) {    var rootExpirationTime = root.expirationTime;    requestWork(root, rootExpirationTime);  }  ...}复制代码

4.scheduleWork先执行一个scheduleWorkToRoot函数, 该函数主要是更新其expirationTime以及上层fiberchildrenExpirationTime

function scheduleWorkToRoot(fiber, expirationTime) {  // Update the source fiber's expiration time  if (fiber.expirationTime === NoWork || fiber.expirationTime > expirationTime) {    fiber.expirationTime = expirationTime;  }  var alternate = fiber.alternate;  if (alternate !== null && (alternate.expirationTime === NoWork || alternate.expirationTime > expirationTime)) {    alternate.expirationTime = expirationTime;  }  // 如果是HostRoot 即直接返回  var node = fiber.return;  if (node === null && fiber.tag === HostRoot) {    return fiber.stateNode;  }  // 若子fiber中有更新, 即更新其childrenExpirationTime  while (node !== null) {    ...  }  return null;}复制代码

5.接着会执行一个markPendingPriorityLevel函数,这个函数主要是更新root的最高优先级和最低优先级(earliestPendingTime和lastestPendingTime;), 同时设置下一个执行操作的时间nextExpirationTimeToWorkOn(即root中具有最高优先级的fiber的expirationTime),关于这个函数的latestSuspendedTime;以后再说

最后scheduleWork会执行requestWork

function requestWork(root, expirationTime) {  addRootToSchedule(root, expirationTime);  if (isRendering) {    // rendering状态,直接返回    return;  }  if (isBatchingUpdates) {    // isBatchingUpdates, 直接返回。 react的state更新是会合并的    ...return;  }  // TODO: Get rid of Sync and use current time?  if (expirationTime === Sync) {    // 执行同步    performSyncWork();  } else {    // 异步, 暂不分析    scheduleCallbackWithExpirationTime(root, expirationTime);  }}复制代码

6.requestWork 会先执行addRootToSchedule,由函数名称可知其作用,将root加到schedule, 即设置firstScheduledRootlastScheduledRoot以及他们的nextScheduleRoot属性,说白了就是一个闭环链式结构 first => next => next => last(next => first), 同时更新rootexpirationTime属性

function addRootToSchedule(root, expirationTime) {   // root尚未开始过任务 将root加到schedule  if (root.nextScheduledRoot === null) {    ...  } else {    // root已经开始执行过任务, 更新root的expirationTime    var remainingExpirationTime = root.expirationTime;    if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) {      root.expirationTime = expirationTime;    }  }}复制代码

7.接着requestWork会判断是否正在渲染中,防止重入。剩余的工作将安排在当前渲染批次的末尾,如果正在渲染直接返回后, 因为已经把root加上到Schedule里面了,依然会把该root执行 同时判断是否正在batch update, 这里留到分析setState的时候说, 最后根据异步或者同步执行不同函数, 此处执行同步performSyncWork(),performSyncWork直接执行performWork(Sync, null);

function performWork(minExpirationTime, dl) {  deadline = dl;  // 找出优先级最高的root  findHighestPriorityRoot();  if (deadline !== null) {    // ...异步  } else {    // 循环执行root任务    while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime)) {      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);      findHighestPriorityRoot();    }  }  ...  // If there's work left over, schedule a new callback.  if (nextFlushedExpirationTime !== NoWork) {    scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);  } ...}复制代码

8.performWork首先执行findHighestPriorityRoot函数。findHighestPriorityRoot函数主要执行两个操作, 一个是判断当前root是否还有任务,如果没有, 则从firstScheuleRoot链中移除。 一个是找出优先级最高的root和其对应的优先级并赋值给 nextFlushedRoot\nextFlushedExpirationTime

function findHighestPriorityRoot() {  var highestPriorityWork = NoWork;  var highestPriorityRoot = null;  if (lastScheduledRoot !== null) {    var previousScheduledRoot = lastScheduledRoot;    var root = firstScheduledRoot;    while (root !== null) {      var remainingExpirationTime = root.expirationTime;      if (remainingExpirationTime === NoWork) {         // 判断是否还有任务并移除      } else {         // 找出最高的优先级root和其对应的优先级      }    }  }  // 赋值  nextFlushedRoot = highestPriorityRoot;  nextFlushedExpirationTime = highestPriorityWork;}复制代码

9.紧着, performWork会根据传入的参数dl来判断进行同步或者异步操作, 这里暂不讨论异步,

while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime)) {      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);      findHighestPriorityRoot();    }复制代码

10.接着, 会进行performWorkOnRoot函数, 并传入优先级最高的root和其对应的expirationTime以及一个true作为参数,performWorkOnRoot函数的第三个参数isExpired主要是用来判断是否已超过执行时间, 由于进行的是同步操作, 所以默认超过 performWorkOnRoot函数会先将rendering状态设为true, 然后判断是否异步或者超时进行操作

function performWorkOnRoot(root, expirationTime, isExpired) {  // 将rendering状态设为true  isRendering = true;  // Check if this is async work or sync/expired work.  if (deadline === null || isExpired) {    // Flush work without yielding.    // 同步    var finishedWork = root.finishedWork;    if (finishedWork !== null) {      // This root is already complete. We can commit it.      completeRoot(root, finishedWork, expirationTime);    } else {      root.finishedWork = null;      // If this root previously suspended, clear its existing timeout, since      // we're about to try rendering again.      var timeoutHandle = root.timeoutHandle;      if (enableSuspense && timeoutHandle !== noTimeout) {        root.timeoutHandle = noTimeout;        // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above        cancelTimeout(timeoutHandle);      }      var isYieldy = false;      renderRoot(root, isYieldy, isExpired);      finishedWork = root.finishedWork;      if (finishedWork !== null) {        // We've completed the root. Commit it.        completeRoot(root, finishedWork, expirationTime);      }    }  } else {    // Flush async work.异步操作    ......    }  }  isRendering = false;}复制代码

11.renderRoot的产物会挂载到rootfinishWork属性上, 首先performWorkOnRoot会先判断rootfinishWork是否不为空, 如果存在的话则直接进入commit的阶段, 否则进入到renderRoot函数, 设置finishWork属性 renderRoot有三个参数, renderRoot(root, isYieldy, isExpired), 同步状态下isYield的值是false, renderRoot 先将 isWorking设为true,

renderRoot会先判断是否是一个从新开始的root, 是的话会重置各个属性

首先是resetStach()函数, 对原有的进行中的root任务中断, 进行存储 紧接着将nextRoot\nextRendeExpirationTime重置, 同时创建第一个nextUnitOfWork, 也就是一个工作单元 这个nextUnitOfWork也是一个workProgress, 也是root.current的alternater属性, 而它的alternate属性则指向了root.current, 形成了一个双缓冲池

if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) {    // 判断是否是一个从新开始的root    resetStack();    nextRoot = root;    nextRenderExpirationTime = expirationTime;    nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime);    root.pendingCommitExpirationTime = NoWork;    ....    ....  }复制代码

12.接着执行wookLoop(isYield)函数, 该函数通过循环执行, 遍历每一个nextUniOfWork,

function workLoop(isYieldy) {  if (!isYieldy) {    // Flush work without yielding    while (nextUnitOfWork !== null) {      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);    }  } else {    // Flush asynchronous work until the deadline runs out of time.    while (nextUnitOfWork !== null && !shouldYield()) {      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);    }  }}复制代码

13.performUnitOfWork 先 获取 参数的alaernate属性, 赋值给current,根据注释的意思, workInProgress是作为一个代替品存在来操作, 然后会执行下面这个语句

next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);复制代码

14.beginWork主要根据workInprogresstag来做不同的处理, 并返回其child, 也就是下一个工作单元 如

, div作为一个工作单元, 处理完后就返回工作单元p, 同时收集他们的
effect

next存在, 则返回到workLoop函数继续循环, 若不存在, 则执行**completeUnitOfWork(workInProgress)**函数

completeUnitOfWork函数, 会判断是否有sibiling, 有则直接返回赋值给next, 否则判断父fiber是否有sibiling, 一直循环到最上层父fiber为null, 执行的同时会把effect逐级传给父fiber

这个时候函数执行完毕, 会返回到renderRoot函数, renderRoot函数继续往下走

首先将isWorking = false;执行, 然后会判断nextUnitWork是否为空, 否的话则将root.finishWork设为空(异步, 该任务未执行完)并结束函数

isWorking = false;if (nextUnitOfWork !== null) { onYield(root); return;}复制代码

重置nextRoot

nextRoot = null;interruptedBy = null;复制代码

赋值finishWork

var rootWorkInProgress = root.current.alternate;onComplete(root, rootWorkInProgress, expirationTime);function onComplete(root, finishedWork, expirationTime) {  root.pendingCommitExpirationTime = expirationTime;  root.finishedWork = finishedWork;}复制代码

15.返回到performWorkOnRoot函数, 进入commit阶段, 将rending状态设为false,返回到performWork函数, 继续进入循环执行root, 直到所有root完成

重置各个状态量, 如果还存在nextFlushedExpirationTime不为空, 则进行scheduleCallbackWithExpirationTime函数异步操作

if (deadline !== null) {    callbackExpirationTime = NoWork;    callbackID = null;  }  // If there's work left over, schedule a new callback.  if (nextFlushedExpirationTime !== NoWork) {    scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);  }  // Clean-up.  deadline = null;  deadlineDidExpire = false;复制代码

结语

以上就是同步模式下的源码分析~

转载地址:http://antpo.baihongyu.com/

你可能感兴趣的文章
oracle 体系结构
查看>>
Nginx+Keepalived搭建高可用负载均衡集群
查看>>
聚合链路及故障排查
查看>>
zabbix监控mysql以及报警(二)终
查看>>
后台服务程序开发模式
查看>>
VS2015 正式版中为什么没有了函数前面引用提示了?
查看>>
windows 系统的安装和虚拟机共享文件夹
查看>>
python—爬虫
查看>>
arp协议的混乱引发的思考--一个实例
查看>>
systcl -p 报错 error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key
查看>>
Why Public Cloud is Not a Security Concern
查看>>
安装centos
查看>>
[C#]创建Windows用户及组
查看>>
一步一步教你使用AgileEAS.NET基础类库进行应用开发-基础篇-UDA中处理事务
查看>>
线程优先级
查看>>
多种方法获取sys_call_table(linux系统调用表)的地址
查看>>
初识SQL Server2017 图数据库(一)
查看>>
分享50款 Android 移动应用程序图标【下篇】
查看>>
软件项目管理流程总结
查看>>
MYSQL企业常见架构与调优
查看>>