先附上两张流程图

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;
}

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

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

接着更新payloadcallback属性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 意思是alternateupdateQueuefiberupdateQueue是同一个对象引用,这里会在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);
}
...
}

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;
}

接着会执行一个markPendingPriorityLevel函数, 这个函数主要是更新root的最高优先级和最低优先级(earliestPendingTime和lastestPendingTime;), 同时设置下一个执行操作的时间nextExpirationTimeToWorkOn(即root中具有最高优先级的fiberexpirationTime),关于这个函数的     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);
}
}

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;
}
}
}

接着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);
}
 ...
}

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;
}

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

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

接着, 会进行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;
}

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.currentalternater属性, 而它的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;
....
....
}
接着执行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);
}
}
}

performUnitOfWork 先 获取 参数的alaernate属性, 赋值给current,

根据注释的意思, workInProgress是作为一个代替品存在来操作, 然后会执行下面这个语句

next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);

beginWork主要根据workInprogresstag来做不同的处理, 并返回其child, 也就是下一个工作单元 如<div><p></p><div>, div作为一个工作单元, 处理完后就返回工作单元p, 同时收集他们的effect

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

completeUnitOfWork函数, 会判断是否有sibiling, 有则直接返回赋值给next, 否则判断父fiber是否有sibiling, 一直循环到最上层 父fibernull, 执行的同时会把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;
}

返回到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;

React Fiber源码分析 第二篇(同步模式)的更多相关文章

  1. React Fiber源码分析 第一篇

    先附上流程图一张 先由babel编译, 调用reactDOM.render,入参为element, container, callback, 打印出来可以看到element,container,cal ...

  2. React Fiber源码分析 (介绍)

    写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~ 文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是: React Fiber是对核心算法 ...

  3. React Fiber源码分析 第三篇(异步状态)

    先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetS ...

  4. 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 百篇博客分析OpenHarmony源码 | v29.01

    百篇博客系列篇.本篇为: v29.xx 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立 ...

  5. JUC源码分析-集合篇(九)SynchronousQueue

    JUC源码分析-集合篇(九)SynchronousQueue SynchronousQueue 是一个同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然.SynchronousQu ...

  6. 死磕以太坊源码分析之Fetcher同步

    死磕以太坊源码分析之Fetcher同步 Fetcher 功能概述 区块数据同步分为被动同步和主动同步: 被动同步是指本地节点收到其他节点的一些广播的消息,然后请求区块信息. 主动同步是指节点主动向其他 ...

  7. 死磕以太坊源码分析之downloader同步

    死磕以太坊源码分析之downloader同步 需要配合注释代码看:https://github.com/blockchainGuide/ 这篇文章篇幅较长,能看下去的是条汉子,建议收藏 希望读者在阅读 ...

  8. 鸿蒙内核源码分析(fork篇) | 一次调用,两次返回 | 百篇博客分析OpenHarmony源码 | v45.03

    百篇博客系列篇.本篇为: v45.xx 鸿蒙内核源码分析(Fork篇) | 一次调用,两次返回 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内 ...

  9. 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 百篇博客分析OpenHarmony源码 | v38.02

    百篇博客系列篇.本篇为: v38.xx 鸿蒙内核源码分析(寄存器篇) | 小强乃宇宙最忙存储器 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...

随机推荐

  1. div+css+position实现简单的纵向导航栏

    完成效果: 这应该是很简单的纵向导航栏了. OK,进入正题 首先,我们看上面的效果图可以分析得出,要实现这个效果需要用到的技术点 1.hover 2.position 3.布局 我认为在这个效果的实现 ...

  2. DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class

    背景: 预告了好久的几篇专栏博文一直没有整理好,主要原因是早前希望搭建的WML服务器计划遇到了问题.起初以为参照DCMTK的官方文档wwwapp.txt结合前两天搭建的WAMP服务器可以顺利的实现WM ...

  3. 【npm】伙计,给我来一杯package.json!不加糖

    前言:夜深了,我熬了一锅热气腾腾的package.json,给大家端上来,希望大家喜欢 json和JS对象的区别 package.json,顾名思义,它是一个json文件,而不能写入JS对象. 所以我 ...

  4. 吴恩达机器学习笔记37-学习曲线(Learning Curves)

    学习曲线就是一种很好的工具,我经常使用学习曲线来判断某一个学习算法是否处于偏差.方差问题.学习曲线是学习算法的一个很好的合理检验(sanity check).学习曲线是将训练集误差和交叉验证集误差作为 ...

  5. 微信小程序的概要

    微信小程序的概要 学习小程序要了解一下什么事小程序,小程序开发前需要做哪些准备,微信小程序开发工具的使用,小程序中的目录结构解析,视图和渲染,事件. 小程序的配置详解,小程序的生命周期与app对象的使 ...

  6. Oracle列转行函数版本不兼容解决方案

    业务场景 本博客记录一下Oracle列转行函数在Oracle11的一些不兼容问题,vm_concat在一些业务场景是必须的.不过这个函数使用要谨慎,底层实现应该也是group by等等实现的,性能并不 ...

  7. webpack vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin

    当我们出现以下报错! 解决方案: // webpack配置文件 const path = require('path'); const htmlWebpackPlugin = require('htm ...

  8. 第13组_16通信3班_045_OSPFv3作业

    IPv6 路由-OSPFv3 实验目的   1. 掌握 OSPFv3 的配置方法 2. 掌握在帧中继环境下 OSPFv3 的配置方法 3. 掌握 OSPFv3 NSSA 的配置方法 4. 学会查看 O ...

  9. sql中base64解码、译码

    1.5.6版本及之后的版本的base64 主要就是两个MySQL内部函数to_base64和from_base64,使用也很简单,如下: 1)先查看MySQL的版本:mysql> select ...

  10. “五年经验”年薪50W分享Java程序员掌握什么技术才不会被淘汰

    在这个IT系统动辄就是上亿流量的时代,Java作为大数据时代应用最广泛的语言,诞生了一批又一批的新技术,包括HBase.Hadoop.MQ.Netty.SpringCloud等等 . 一些独角兽公司以 ...