1.概念
整体的更新流程引入fiber后拆分成啦以fiber为结构的跟新流程,且我们可以按优先级更新和中断。
主要是ReactFiberScheduler.js文件这里面有很多公共变量 我们对这些变量的理解和他们再别处被调用修改的值的理解是关键。
大概流程:
总结:保证我们低优先级的跟新不会影响浏览器的动画等渲染
2.schedulework
1.找到更新对应的fiberRoot节点 因为我们setState和forceupdate时候传过去的fiber都是对应的组件
2.如果符合一定条件就重置stack 立马存储一些重要变量
3.如果符合条件就请求工作调度
ps:我们Button点击 是list组件调用setState 但是是我们的rootFiber加入调度队列中的,加入调度的都是root节点 因为跟新的开始都是root节点
3.batchupdates
//requestwork.js
if (isBatchingUpdates) {//!批处理相关知识
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, true);
}
return; //如果是批量更新直接return 不执行下面的performSyncWork,scheduleCallbackWithExpirationTime
}
// TODO: Get rid of Sync and use current time?
//!更具expirationTime的类型去调用相关调度方法
if (expirationTime === Sync) {
performSyncWork();//!同步 无法打断
} else {
scheduleCallbackWithExpirationTime(root, expirationTime);//!异步
}
让我们isBatchingUpdates = true 然后再requestwork里面直接return 不去执行任务,然后再最后一起执行sync的更新fiber的updateQueue一次执行。
所有的事件回调函数都会走batchupdates逻辑
function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
const previousIsBatchingUpdates = isBatchingUpdates;
isBatchingUpdates = true;
try {
return fn(a);//!执行事件的回调函数 等里面所有setState都加入到udpateQueue中
} finally {
//!判断当前是否要batchUpdates
isBatchingUpdates = previousIsBatchingUpdates;
if (!isBatchingUpdates && !isRendering) {
performSyncWork();//!同步方式执行 代替requestWork里面执行更新
}
}
}
普通节点渲染 调用unbatchedUpdates isBatchingUpdates为false 然后update走到fiber的queue里面 最后再执行requestwork 这时候isBatchingUpdates为false 就去分情况任务调度
//!批量更新
DOMRenderer.unbatchedUpdates(() => {
if (parentComponent != null) {
root.legacy_renderSubtreeIntoContainer(
parentComponent,
children,
callback,
);
} else {
//!调用render方法 root是ReactRoot对象
root.render(children, callback);//!children是渲染到container的孩子节点
}
});
//unbatchedUpdates
function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
if (isBatchingUpdates && !isUnbatchingUpdates) {
isUnbatchingUpdates = true;
try {
return fn(a);
} finally {
isUnbatchingUpdates = false;
}
}
return fn(a);
}
isbatching只有再事件回调函数或者自己调用batchupdate时候 开始会把isbatching为true 任务结束后变为false 所以其他地方比如(dom第一次渲染)就是false
事件相关的是再interactiveUpdates$1函数
setTimeout(() => {
this.countNumber()
}, 0)
这种情况下整个函数是在fn里面执行 这时候只是读取了settimeout 还没执行里面回调 就退出了batchupdates(相当于只执行啦读取setTimeout 但是里面的函数包括setState都是异步执行 那后面触发setTimeout就不会走batchupdates,而是直接走requestWork) 但是setTimeout的回调函数是后面自己执行 这时候就会直接去requestWork 那么此时batchupdates为false 就会去走同步任务刷新视图
setTimeout(() => {
batchedUpdates(() => this.countNumber())
}, 0)
这个代码当回掉执行的时候 不是直接去requestWork 而是再走batchedUpdates去设置全局batchUodates为true 这时候又是直接return 在finaly里面走performSync
isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;
isBatchingUpdates = previousIsBatchingUpdates;
if (!isBatchingUpdates && !isRendering) {
performSyncWork();
}
这部分逻辑为什么取之前的previousIsBatchingInteractiveUpdates 因为就怕之前是true 那么就表示外层的还是在批量更新过程中 因为批量结束后一定是false. 如果还在更新过程中 我们就直接return 让他们在最外层的批量更新里去刷新所有的updateQueue队列。
记住直接的绑定事件有两层 1.intertive 2.batchupdates 在第二层时候调用prebatchupdates即外层的batchupdatese为true 所以会在最外层去performSyncWork
直接调用batchedUpdates 只有一层 这时候prebatchupdates即外层的batchupdatese为false 所以这一层去performSyncWork
setState第二个参数是以回调函数形式存放到update的callback中 在updateQueue执行完再去执行
**ps:**异步setState函数是自己进入requestWork的 事件都封装啦batch但是异步函数只会读取最外层的函数没执行里面的函数 所以里面执行的时候直接去requestWork就没batch啦
流程图