React16源码: React中的schedule调度整体流程
schedule调度的整体流程
- React Fiber Scheduler 是 react16 最核心的一部分,这块在
react-reconciler
这个包中 - 这个包的核心是 fiber reconciler,也即是 fiber 结构
- fiber 的结构帮助我们把react整个树的应用,更新的流程,能够拆成每一个 fiber 对象为单元的一个更新的流程
- 这种单元的形式把更新拆分出来之后,给每个不同的任务提供一个优先级,以及我们在更新的过程当中,可以中断
- 因为我们记录更新到了哪一个单元,中断了之后,可以过一会儿再回过头来,继续从这个单元开始,继续之前没有做完的更新
- 在 react 16之前 setState 产生的更新,必须从头到尾更新完成,然后再执行之后的代码
- 如果我们的整个应用树,它的节点非常多,整个更新会导致它占用的js的运行时间会非常的多
- 让页面的其他的一些操作进入一个停滞的状态
- 比如说,动画的刷新或者是我们在 input 里面输入内容的时候,可能产生卡顿的感觉
- 所以,react 16之后,它的整体的更新流程是完全不一样的
- 因为加入了中断,挂起,这样的功能,导致它的整个更新流程的调度变得非常的复杂
- 整个源码体系,它每一个细节,每一个变量的具体作用,都是值得琢磨的
- 理解它出于什么目的这么去设计,这时候才能深入整个react的更新体系中,这样才能慢慢理解
全局变量一览
- 调度过程中的全局变量,基本上都在 react-reconciler/src/ReactFiberScheduler.js 这个js里面
- https://github.com/facebook/react/blob/v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js
- 这个js的代码是非常多的,它总共有两千五百行代码,而且注释不算多
- 在这个文件里面,会存在着非常多的公共变量,就是说我们定义在这个文件的顶层的变量名
- 就是说我们定义在这个文件顶层作用域上面的很多的变量,在很多方法里面,它们是被共享的
- 这些公共变量对于去理解整个 schedule,它是非常重要的,因为它在很多方法里面都会有调用
- 它什么时候调用,什么时候被修改成什么值,用来记录什么内容
- 对于这些公共变量的理解,一方面来说比较的困难
- 另外一方面来说,它非常的重要
- 如果不能理解这些公共变量的作用,会导致看源码的时候,看到一些地方会变得毫无头绪
几个重点需要关注的变量
// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
let lastUniqueAsyncExpiration: number = Sync - 1;let isWorking: boolean = false;// The next work in progress fiber that we're currently working on.
let nextUnitOfWork: Fiber | null = null;
let nextRoot: FiberRoot | null = null;
// The time at which we're currently rendering work.
let nextRenderExpirationTime: ExpirationTime = NoWork;
let nextLatestAbsoluteTimeoutMs: number = -1;
let nextRenderDidError: boolean = false;// The next fiber with an effect that we're currently committing.
let nextEffect: Fiber | null = null;let isCommitting: boolean = false;
let rootWithPendingPassiveEffects: FiberRoot | null = null;
let passiveEffectCallbackHandle: * = null;
let passiveEffectCallback: * = null;let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;// Used for performance tracking.
let interruptedBy: Fiber | null = null;let stashedWorkInProgressProperties;
let replayUnitOfWork;
let mayReplayFailedUnitOfWork;
let isReplayingFailedUnitOfWork;
let originalReplayError;
let rethrowOriginalError;
-
isWorking
- commitRoot和renderRoot开始都会设置为true,然后在他们各自阶段结束的时候都重置为false
- 用来标志是否当前有更新正在进行,不区分阶段
-
isCommitting
- commitRoot开头设置为true,结束之后设置为false
- 用来标志是否处于commit阶段
-
nextUnitOfWork
- 用于记录render阶段Fiber树遍历过程中下一个需要执行的节点。
- 在resetStack中分别被重置
- 它只会指向workInProgress
-
nextRoot & nextRenderExpirationTime
- 用于记录下一个将要渲染的root节点和下一个要渲染的任务的
-
nextEffect
- 用于commit阶段记录firstEffect -> lastEffect链遍历过程中的每一个Fiber
下面是其他的一些全局变量
// Linked-list of roots
let firstScheduledRoot: FiberRoot | null = null;
let lastScheduledRoot: FiberRoot | null = null;let callbackExpirationTime: ExpirationTime = NoWork;
let callbackID: *;
let isRendering: boolean = false;
let nextFlushedRoot: FiberRoot | null = null;
let nextFlushedExpirationTime: ExpirationTime = NoWork;
let lowestPriorityPendingInteractiveExpirationTime: ExpirationTime = NoWork;
let hasUnhandledError: boolean = false;
let unhandledError: mixed | null = null;let isBatchingUpdates: boolean = false;
let isUnbatchingUpdates: boolean = false;let completedBatches: Array<Batch> | null = null;let originalStartTimeMs: number = now();
let currentRendererTime: ExpirationTime = msToExpirationTime(originalStartTimeMs,
);
let currentSchedulerTime: ExpirationTime = currentRendererTime;// Use these to prevent an infinite loop of nested updates
const NESTED_UPDATE_LIMIT = 50;
let nestedUpdateCount: number = 0;
let lastCommittedRootDuringThisBatch: FiberRoot | null = null;
-
firstScheduledRoot & lastScheduledRoot
- 用于存放有任务的所有root的单链表结构
- 在findHighestPriorityRoot用来检索优先级最高的root
- 在addRootToSchedule中会修改
-
callbackExpirationTime & callbackID
- 记录请求ReactScheduler的时候用的过期时间,如果在一次调度期间有新的调度请求进来了
- 而且优先级更高,那么需要取消上一次请求,如果更低则无需再次请求调度。
- callbackID是ReactScheduler返回的用于取消调度的 ID
-
isRendering
- performWorkOnRoot开始设置为true,结束的时候设置为false
- 表示进入渲染阶段,这是包含render和commit阶段的
-
nextFlushedRoot & nextFlushedExpirationTime
- 用来标志下一个需要渲染的root和对应的expirtaionTime
-
deadline & deadlineDidExpire
- deadline是ReactScheduler中返回的时间片调度信息对象
- 用于记录是否时间片调度是否过期,在shouldYield根据deadline是否过期来设置
-
isBatchingUpdates & isUnbatchingUpdates & isBatchingInteractiveUpdates
- batchedUpdates、unBatchedUpdates,deferredUpdates、interactiveUpdates等这些方法用来存储更新产生的上下文的变量
-
originalStartTimeMs
- 固定值,js 加载完一开始计算的时间戳
-
currentRendererTime & currentSchedulerTime
- 计算从页面加载到现在为止的毫秒数,后者会在isRendering === true的时候
- 用作固定值返回,不然每次requestCurrentTime都会重新计算新的时间。
-
以上,每一个全局变量给它拿出来,单独解释它是在什么地方被用到
-
以及它是用来记录哪些东西,是在什么情况下,才会发挥了哪些作用
调度流程
1 )第一阶段

- 在调用
ReactDOM.render
,setState
,forceUpdate
,都会产生一个update - 产生update之后,进入 scheduleWork 进行调度
- scheduleWork 第一步操作是
addRootToScheduler
- 在一个rect应用当中,它不仅仅可能只存在一个 root 节点
- 因为我们通过
ReactDOM.render
调用的时候,就会创建一个 root 节点 - 如果调用多次
ReactDOM.render
,就可以创建多个 root 节点 - 这个时候, 整个应用中就会存在着多个react的节点
- 在这些节点,可以单独在内部进行 setState,进行调度
- 它们都会有独立的 updateQueen,有独立的一个 fiber tree 来进行应用的更新
- 一个应用当中可能会存在多个root, 所以这个时候就要去维护一下
- 因为,在同一时间可能有多个root会有更新存在, 所以有这么一个地方去维护它
- 这就是
addRootToScheduler
的一个作用
- scheduleWork 第一步操作是
addRootToScheduler
加入之后, 要先判断一下是否正在 render 阶段 或者 前后的root不同- 如果是,则调用
requestWork
,就要开始进行工作了,如果不是,我们就要return
- 因为之前的任务可能正在做,或者处于目前这个阶段,不需要主动的再去调用一个
requestWork
来更新了
- 如果是,则调用
- 关于
requestWork
它里面做了什么?- 它判断
expirationTime
,它是否是Sync
- 计算
expirationTime
调用的是computeExpirationForFiber
- 这时候会根据 fiber 是否有
ConcurrentMode
的特性来计算 Sync 的 expirationTime 或者是异步的 expirationTime - 这个时候它最终会导致整体的一个更新模式的不同
- 因为如果是 Sync 的模式代表着我们这个更新要立马进行执行,要立马更新到最终的 dom tree 上面
- 所以我们调用的是
performSyncWork
- 而如果它是一个 Async 模式的,那么说明它的优先级不是特别高,那么他会进入一个调度的流程,因为它可以不立即更新
- 它本身的期望是在 expirationTime 结束之前,能够被更新完成就可以了, 所以它的优先级非常低,会进入到一整个调度的流程,即
scheduleCallbackWithExpirationTime
- 它判断
2 )下一阶段

- 整个调度的流程 react 给它单独区分了一个包
packages/scheduler
- 用蓝色的框给它圈起来,叫做 async schedule work
- 这一部分就涉及到整个异步的调度的过程
- 它利用的是浏览器当中一个较新的API叫做
requestIdleCallback
, 能够让浏览器优先进行他自己的任务 - 比如说更新动画,在每一帧有多余的时间的时候,它调用react给他设置了一个callback
- 然后就可以去执行react一个更新,然后react会自己去记一个时
- 在这个时间内,我可以执行我自己的工作
- 如果这个时间内我的工作没有执行完,我要把javascript的运行的主动权交还给浏览器
- 让浏览器去执行它新的一些动画的更新之类的,来让浏览器保证高优先级的任务能够被立即执行
- 所以,这就是这个蓝色这一片区域的一个作用
- 它利用的是浏览器当中一个较新的API叫做
- 在这里面调用的一个方法叫做
scheduleDeferredCallback
, 然后会有一个 callbackList - 因为可能多次调用这个方法去把 callback 设置进去
- 然后在这里面,我们虽然想要使用
requestIdleCallback
这个API - 但是, 大部分浏览器还不支持, 浏览器的兼容性也不是特别好
- 所以在react里面,它实现了自己的一个模拟 requestIdleCallback 的一个方式
- 它通过
requestAnimationFrame
和js
的任务队列的原理来进行了一个模拟
- 它通过
- 在调用
requestIdleCallback
之后,说明浏览器有空了,可以去执行react的更新了- 这就是我们加入到这里面的异步的更新任务,它的优先级比较低,浏览器有空的时候,再来执行
- 因为 react 的任务它是有一个 expirationTime 的
- 所以它这里要判断一下我的任务有没有超时
- 如果已经超时了,要把所有加入callbackList队列的超时任务都执行掉
- 因为任务已经超时了,所以必须要立刻完成
- 执行到第一个非超时的任务之后,若还有时间,可以继续执行
- 如果没有时间了,要把主动权交还给浏览器,让浏览器来做其他一些任务
- 最终要执行这个任务,执行的是什么?
- 调用一个
performAsyncWork
这个方法 - 它会执行react的schedule里面的回调函数
- 在调用这个方法的时候,schedule 会传给这个方法一个叫做deadline的一个对象,这个对象是用来判断。
- 在进入
performAsyncWork
的时候,就进入到react的一个更新流程 - react的更新流程中,它去遍历整一棵树,会遍历每棵树的每个单元,然后对它进行一个更新的操作
- 每个单元更新完了之后,回过头来通过这个deadline对象判断一下,现在是否还有 js 运行的一个时间片
- 因为调度器每一次调用
performAsyncWork
的任务,它是有一个时间限制的 - 比如说默认的情况下是22毫秒,在这个时间片内你可以去执行的操作
- 这就是这个 deadline 对象它的一个作用
- 调用一个
- 最终调用
performWork
这个方法 performWork
它调用的是没有deadline的performAsyncWork
它调用的是有deadline的- 最终在 if deadline 这里汇集在一起
- 根据是否有 deadline 进入下个阶段的循环
3 )第三个阶段

- 根据是否有 deadline 进入循环
- 这个循环是什么呢?
- 这个循环就是要遍历整棵树每一个 fiber 节点进行的更新操作
- 对于同步任务,它会遍历完整棵树,然后把整个应用更新就完了
- 因为它是同步的,跟以前的react用法是一样的
- 对于异步的来讲,如果符合条件
- 进入
performWorkOnRoot
, 它做的其实是一个更新的过程 - 然后
findHighestPriorityRoot
找到一个最高优先级的节点之后 - 对这个节点进行一个更新
recomputeCurrentRendererTime
- 对于有 deadline 的情况,调用
performWorkOnRoot
进行更新任务之后 - 在
renderRoot
里面,它还会有一个循环去判断 deadline - 最终要等这个
performWorkOnRoot
返回之后,才会继续下面的操作 - 对于有 deadline 的情况,会重新请求一个时间,然后去判断一下deadline是否已经过期
- 如果已经过期的话,会回过头来到红色区域再进行一个判断
- 如果发现 deadline 已经过期的话,又会去继续调用这个
scheduleCallbackWithExpirationTime
- 再次进行异步的回调, 它这是一个递归的过程
- 因为之前第一阶段加入了一个
addRootToScheduler
- 它就有一个队列,在维护着所有的更新的情况
- 对于每一次更新,只能更新一个优先级的任务,以及一个root上的任务
- 上述红色这块区域,它就是一个循环的条件判断
- 它每次更新一个节点上的一个优先级任务
- 具体的操作在
performWorkOnRoot
里面 - 更新完之后, 它会去调用相对应的方法
- 在这个root上对应的优先级任务更新完之后
- 它要找到下一个root上面的对应优先级的任务,然后再次进入这个循环
- 所以这个就是deadline它的一个用处,它帮助我们去判断是否应该跳出循环了
- 如果我们一直处于这个循环,要把所有任务都更新完,那么可能占用的js运行时间会非常的长
- 导致可能的动画停滞或用户输入卡顿。
- 在这个 deadline 超过了之后,这个循环直接跳出
- 然后再继续跳回到这个
scheduleCallbackWithExpirationTime
- 再次进入一个调度,然后把js的执行权交给浏览器,让它先执行动画或者用户输入的响应
- 等有空了,再回过头来再去执行这个任务,然后又回到 红色区域判断这里
- 之前没完成的任务,再继续这么一个循环
- 最终达到的目的是要把 root 里面的所有的节点上面的所有更新都执行完为止
- 这就是整的一个循环的一个过程
- 进入
整体流程图

总结
- 通过
ReactDOM.render
,setState
,forceUpdate
这几个方法产生了更新 - 产生了更新之后,维护一个队列去保存这些更新以及对应的root节点
- 然后,根据它任务的优先级来进行判断,判断是同步的更新还是异步的更新
- 对于异步的更新,如果有多个任务,它会一直处于先执行浏览器优先级最高的更新
- 有空的时候回过头来更新 react 树
- 如果 root 上对应的某一个优先级的任务更新完了
- 那么先输出到dom上,然后执行下一个更新
- 在这个循环的过程当中,根据这个调度器传入的 deadline 对象
- 判断是否有超时,如果超时,再回过头去进行一个调度
- 先把执行权给浏览器,让它保证动画的流畅运行
- 等它有空,再回过头来继续执行的任务
- 这就是整个调度的核心原理
- 目的是
- 保证我们低优先级的react更新,不会阻止浏览器的一些高要求的动画更新
- 能够保证浏览器的一些动画能够达到30帧以上这么一个情况
- 以上就是react整个的调度过程
相关文章:

React16源码: React中的schedule调度整体流程
schedule调度的整体流程 React Fiber Scheduler 是 react16 最核心的一部分,这块在 react-reconciler 这个包中这个包的核心是 fiber reconciler,也即是 fiber 结构fiber 的结构帮助我们把react整个树的应用,更新的流程,能够拆成…...

springboot mybatis-plus swing实现报警监听
通过声音控制报警器,实现声光报警,使用beautyeye_lnf.jar美化界面如下 EnableTransactionManagement(proxyTargetClass true) SpringBootApplication EnableScheduling public class AlarmWarnApplication {public static void main(String[] args) …...

【计算机网络】网络层——详解IP协议
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得,欢迎大家在评论区交流讨论💌 目录 🐱一、I…...

【Java数据结构】03-二叉树,树和森林
4 二叉树、树和森林 重点章节,在选择,填空,综合中都有考察到。 4.1 掌握二叉树、树和森林的定义以及它们之间的异同点 1. 二叉树(Binary Tree) 定义: 二叉树是一种特殊的树结构,其中每个节点…...
Element UI Input组件内容格式化:换行时行首添加圆点
<el-input v-model"input"placeholder"请输入"type"textarea":rows"8"focus"handleFocus"input.native"handleInput" /> 解释一下: Element UI对 input 事件做了一层包装,无法返回…...

十、Qt 操作PDF文件
《一、QT的前世今生》 《二、QT下载、安装及问题解决(windows系统)》《三、Qt Creator使用》 《四、Qt 的第一个demo-CSDN博客》 《五、带登录窗体的demo》 《六、新建窗体时,几种窗体的区别》 《七、Qt 信号和槽》 《八、Qt C 毕业设计》 《九、Qt …...
开源软件合规风险与开源协议的法律效力
更多内容:OWASP TOP 10 之敏感数据泄露 OWASP TOP 10 之失效的访问控制 OWASP TOP 10 之失效的身份认证 一、开源软件主要合规风险 1、版权侵权风险 没有履行开源许可证规定的协议导致的版权侵权,例如没有按照许可要求的保留…...

2024全新开发API接口调用管理系统网站源码 附教程
2024全新开发API接口调用管理系统网站源码 附教程 用layui框架写的 个人感觉很简洁 方便使用和二次开发...

[Linux 进程(四)] 再谈环境变量,程序地址空间初识
文章目录 1、前言2、环境变量2.1 main函数第三个参数 -- 环境参数表2.2 本地环境变量和env中的环境变量2.3 配置文件与环境变量的全局性2.4 内建命令与常规命令2.5 环境变量相关的命令 3、程序地址空间 1、前言 上一篇我们讲了环境变量,如果有不明白的先读一下上一…...
【C++】STL(标准模板库)
文章目录 1. 基本概念2. 容器2.1. 容器的分类2.2. vector2.2.1. 构造vector对象2.2.2. vector的赋值 1. 基本概念 STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称,现在已经成为C标准库的重要组成部分。STL的…...

【已解决】fatal: Authentication failed for ‘https://github.com/.../‘
文章目录 异常原因解决方法 异常原因 在 Linux 服务器上使用git push命令,输入用户名和密码之后,总会显示一个报错: fatal: Authentication failed for https://github.com/TianJiaQi-Code/Linux.git/ # 致命:无法通过验证访问起…...

SqlAlchemy使用教程(二) 入门示例及编程步骤
SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解 二、入门示例与基本编程步骤 在第一章中提到,Sqlalchemy提供了两套方法来访问数据库,由于Sqlalchemy 官方文档结构有些乱,对于ORM的使用步骤的描…...
HTML+JS+CSS移动端购物车选购界面
代码打包资源下载:【免费】HTMLJSCSS移动端购物车选购界面资源-CSDN文库 关键部分说明: UIGoods 类: 构造函数: 创建 UIGoods 实例时,传入商品数据 g,初始化商品的数据和选择数量。getTotalPrice() 方法…...
微服务治理:为什么要分析微服务的依赖关系?
在微服务架构中,单个服务相互协作以交付功能。这些协作会在服务之间形成依赖关系,其中一个服务依靠另一个服务来完成自己的任务。虽然依赖关系使功能得以实现,但不受控制的依赖关系可能会导致一系列挑战: 复杂性: 错综复杂的依赖…...
【程序员的自我修养—系统调用与API】
系统调用 背景: 为了避免有限的系统资源被多个不同的应用程序同时访问,需要加以保护,避免冲突;提供一套统一的接口,是应用程序能做一些由操作系统支持的行为;接口通过中断的方式实现,Linux使用…...

使用宝塔面板部署后端项目到服务器
文章目录 前言第一步:安装数据库第二步:打包后端项目第三步:配置数据库第四步:部署后端项目第五步:前后端联调测试总结 前言 在之前我已经写了一篇如何去部署前端项目,虽然能访问网站,但是没有…...

走迷宫(c语言)
前言: 制作一个迷宫游戏是一个有趣的编程挑战。首先,我们需要设计一个二维数组来表示迷宫的布局,其中每个元素代表迷宫中的一个格子。我们可以使用不同的值来表示空格、墙壁和起点/终点。接下来,我们需生成迷宫。在生成迷宫的过程…...

两周掌握Vue3(五):自定义指令、路由、ajax
文章目录 一、自定义指令1.创建和使用自定义指令2.钩子函数3.使用参数 二、路由1.创建一个router实例2.在components目录中创建组件3.将路由实例挂载到应用4.使用路由 三、Ajax 代码仓库:跳转 当前分支:05 一、自定义指令 自定义指令是Vue.js框架提供的…...

redis之单线程和多线程
目录 1、redis的发展史 2、redis为什么选择单线程? 3、主线程和Io线程是怎么协作完成请求处理的? 4、IO多路复用 5、开启redis多线程 1、redis的发展史 Redis4.0之前是用的单线程,4.0以后逐渐支持多线程 Redis4.0之前一直采用单线程的主…...

12AOP面向切面编程/GoF之代理模式
先看一个例子: 声明一个接口: // - * / 运算的标准接口! public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j); }实现该接口: package com.sunsplanter.prox…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...