更新流程和优先级
在dispatchAction 中创建update对象
在函数组件中,多个hooks会组成单向链表,每一个hook中会有个生成好环状链表以后调用schedulUpdateOnFiber方法
schedulUpdateOnFiber:在这个fiber中调度这个update
从fiber找到root以后,使用 markUpdateLaneFromFiberToRoot 会从该fiber节点一直向上查找,
调用 markRootUpdated
更新流程大概这个流程。
优先级的种类:
NoPriority = 0; // 没有优先级
ImmediatePriority = 1; // 立即执行优先级
UserBlockingPriority = 2; // 用户交互优先级
NormalPriority = 3; // 普通优先级
LowPriority = 4; // 低优先级
IdlePriority = 5; // 可中断优先级
在整个更新过程中,如果一个触发了一个普通的setState,会触发一个优先级为NormalPriority的更新,接下来会一直向上查找直到root,在fiberRoot上会保存一个NormalPriority,已NormalPriority优先级 调度整个应用的根节点。
我们简单看一个代码来分析页面表现(concurrent mode):
import { useState, useRef, useEffect } from 'react';
export defult () => {
const [count, updateCount] = useState(0);
const buttonRef = useRef(null)
const onclick = () => {
updateCount(count => count + 2)
}
useEffect(() => {
setTimeout(() => { updateCount(1) }, 1000)
setTimeout(() => buttonRef.current?.click(), 1001)
}, [])
return (
<div className="App">
<button ref={buttonRef} onClick={onclick}>增加2</button>
<div>{
Array.from(new Array(15000)).map((item, index) => <span key={index}>{count}</span>)
}</div>
</div>
);
}
这是一个高优先级任务抢占低优先级任务的问题:
在刚开始渲染页面的时候初始状态为0 页面渲染15000个0,这个没有问题。
在过1000毫秒以后,会有一个NormalPriority优先级的更新 updateCount(1),该更新找到root以后,产生更新调度,有15000个fiber节点要对比更新。然后这个是一个耗时的操作。
在1001毫秒的时候,又产生一个UserBlockingPriority优先级的更新buttonRef.current?.click(),此时上面的更新被打断跳过。
baseState: 0 结合 updateCount(count => count + 2) 产生 memoizedState: 2。页面会先渲染出2。
下次更新为跳过的update 和 后面的所有的update,baseState为跳过是的值为0。所有会有updateCount(1) 和 updateCount(count => count + 2),产生 memoizedState: 3,页面会渲染出3。
this.setSetate流程
当在组件中调用this.setState的时候,会进入
Component.prototype.setState = (state, callback) => {
this.updater.enqueueState(this, state, callback)
}
而enqueueState方法:
会根据当前实例获取当前的fiber,
接着获取当前时间,
然后获取lane优先级相关的,
然后创建update,update.payload = state
enqueueSetState: function (inst, payload, callback) {
var fiber = get(inst);
var eventTime = requestEventTime();
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback;
}
enqueueUpdate(fiber, update);
var root = scheduleUpdateOnFiber(fiber, lane, eventTime);
markStateUpdateScheduled(fiber, lane);
},
所以就是回到了文章开头的更新流程了。
花未全开月未圆
半山微醉尽余欢
何须多虑盈亏事
终归小满胜万全