You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunctionenqueueUpdate<State>(fiber: Fiber,update: Update<State>){// Update queues are created lazily.constalternate=fiber.alternate;letqueue1;letqueue2;// 通过 ReactDOM.render 初次渲染if(alternate===null){// There's only one fiber.
queue1 =fiber.updateQueue;queue2=null;// 创建一个updateQueueif(queue1===null){
queue1 =fiber.updateQueue=createUpdateQueue(fiber.memoizedState);}}else{// There are two owners.
queue1 =fiber.updateQueue;queue2=alternate.updateQueue;if(queue1===null){if(queue2===null){// Neither fiber has an update queue. Create new ones.queue1=fiber.updateQueue=createUpdateQueue(fiber.memoizedState);queue2=alternate.updateQueue=createUpdateQueue(alternate.memoizedState,);}else{// Only one fiber has an update queue. Clone to create a new one.
queue1 =fiber.updateQueue=cloneUpdateQueue(queue2);}}else{if(queue2 ===null){// Only one fiber has an update queue. Clone to create a new one.
queue2 =alternate.updateQueue=cloneUpdateQueue(queue1);}else{// Both owners have an update queue.}}}// 初次渲染 queue2 为nullif(queue2===null||queue1===queue2){// There's only a single queue.// 将 update 加入到 updateQueue 里// fiber.updateQueue.firstUpdate = fiber.updateQueue.lastUpdate = update;appendUpdateToQueue(queue1,update);}else{// There are two queues. We need to append the update to both queues,// while accounting for the persistent structure of the list — we don't// want the same update to be added multiple times.if(queue1.lastUpdate===null||queue2.lastUpdate===null){// One of the queues is not empty. We must add the update to both queues.appendUpdateToQueue(queue1,update);appendUpdateToQueue(queue2,update);}else{// 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.
appendUpdateToQueue(queue1,update);// But we still need to update the `lastUpdate` pointer of queue2.queue2.lastUpdate =update;}}}
functionappendUpdateToQueue<State>(
queue: UpdateQueue<State>,
update: Update<State>,
) {// Append the update to the end of the list.if(queue.lastUpdate===null){// Queue is emptyqueue.firstUpdate=queue.lastUpdate=update;} else {// firstUpdate.next.next.next .... 为单链表结构// 上次的 next 和 这次的 lastUpdate 都指向新的 update,改变的是 lastUpdate 指针queue.lastUpdate.next=update;// 每次更新 lastUpdatequeue.lastUpdate=update;}}
functionrequestCurrentTime(){// 已经进入渲染的阶段if(isRendering){returncurrentSchedulerTime;}// ReactDOM.render 执行if(nextFlushedExpirationTime===NoWork||nextFlushedExpirationTime===Never){recomputeCurrentRendererTime();currentSchedulerTime=currentRendererTime;returncurrentSchedulerTime;}returncurrentSchedulerTime;}functionrecomputeCurrentRendererTime(){constcurrentTimeMs=now()-originalStartTimeMs;currentRendererTime=msToExpirationTime(currentTimeMs);}constUNIT_SIZE=10;constMAGIC_NUMBER_OFFSET=2;// 1 unit of expiration time represents 10ms.exportfunctionmsToExpirationTime(ms: number): ExpirationTime{// 除10 向下取整return((ms/UNIT_SIZE)|0)+MAGIC_NUMBER_OFFSET;}
functioncomputeExpirationForFiber(currentTime: ExpirationTime,fiber: Fiber){letexpirationTime;// 外部强制的情况// 两种情况赋值 expirationContext ,// 1、deferredUpdates 计算出 Async 方式的优先级时间// 2、syncUpdates <- flushSync <- ReactDOM.flushSync , syncUpdates 在执行 fn 之前将 // expirationContext 修改为 Sync,通过外部强制某个更新必须使用那种 expirationTime 的行为if(expirationContext!==NoWork){// An explicit expiration context was set;expirationTime=expirationContext;}elseif(isWorking){// 有任务更新的情况if(isCommitting){// Updates that occur during the commit phase should have sync priority// by default.expirationTime=Sync;}else{// Updates during the render phase should expire at the same time as// the work that is being rendered.expirationTime=nextRenderExpirationTime;}}else{// No explicit expiration context was set, and we're not currently// performing work. Calculate a new expiration time.// ConcurrentMode = 0b001; 通过 与 / 或运算便于组合和判断不同的mode// 异步 mode 才计算 expirationTimeif(fiber.mode&ConcurrentMode){// interactiveUpdates 函数里置为true,即计算高优先级的expirationTimeif(isBatchingInteractiveUpdates){// This is an interactive updateexpirationTime=computeInteractiveExpiration(currentTime);}else{// This is an async updateexpirationTime=computeAsyncExpiration(currentTime);}// If we're in the middle of rendering a tree, do not update at the same// expiration time that is already rendering.if(nextRoot!==null&&expirationTime===nextRenderExpirationTime){expirationTime+=1;}// 同步的更新}else{// This is a sync updateexpirationTime=Sync;}}if(isBatchingInteractiveUpdates){// ...}returnexpirationTime;}
FiberRoot 与 RootFiber
FiberRoot
FiberRoot对象
FiberRoot
是ReactRoot
生成实例时调用ReactFiberReconciler.js
的createContainer
传入getElementById(root)
执行ReactFiberRoot.js
中的createFiberRoot
函数生成的Fiber
ReactElement
对应一个Fiber
对象class Component
中的this.state
、this.props
,在 Fiber 更新后才会更新class Component
上的this.state
,props
,也是hooks
实现的原理,functional Component
是没有this.state
this.props
的,Fiber
有能力记录这些状态之后在functional Component
更新后拿到这些状态。ReactElement
对应的树结构Fiber
数据结构ReactElement 对应的Fiber的结构
update 和 updateQueue
用于记录组件状态的改变,记录改变的方式和内容
存放于
updateQueue
中,存放多个update
用来计算出最终改变的结果多个
update
可以同时存在,setState
三次会创建三个update
,放到updateQueue
里,在进行更新的操作update 数据结构
ReactDOM.render
最终调用ReactRoot.prototype.render
时会执行到scheduleRootUpdate
方法里执行createUpdate
updateQueue 数据结构
enqueueUpdate 方法
enqueueUpdated
就是在fiber
对象上创建一个updateQueue
,然后把update
对象传入到这个queue
里appendUpdateToQueue
方法,初次渲染将queue
的firstUpdate
和lastUpdate
都指向update
,之后的每有更新创建都将lastUpdate.next
指向新的update
,将lastUpdate
指向新的update
构建链表结构expirationTime
在
updateContainer
方法中会计算一个expirationTime
然后用这个时间创建update
对象推入updateQueue
内requestCurrentTime
方法返回一个固定的常量,调用recomputeCurrentRendererTime
计算js加载完成到当前渲染时间的时间差值,这个差值范围小(没超过一个单位UNIT_SIZE)的值会在msToExpirationTime
内被计算成同一个常数,最后赋值全局变量currentRendererTime
computeExpirationForFiber
方法会根据当前渲染的 currentTime 计算出一个优先级时间,核心就是根据渲染方式的 mode 不同来创建不同优先级的 expirationTime, 区别在于传入computeExpirationBucket 的参数不同。computeExpirationBucket
最终的公式为((((currentTime - 2 + 5000(或者150) / 10) / 25(或者10)) | 0) + 1) * 25(或者10),
用| 0
向下取整,使得在没超过一个单位 25(10)范围内的时间计算出来的值都相同,这样在很短时间内多次 setState 调用更新时,也可以保证是同一优先级的更新。上一篇
下一篇
The text was updated successfully, but these errors were encountered: