function SlowPost({ data }) {
let startTime = performance.now();
while (performance.now() - startTime < 1) {
// Do nothing for 1 ms per item to emulate extremely slow code
}
return (
<li className="item">
Post #{data}
</li>
);
}
export default function Index(){
const [ number , setNumber ] = useState(0)
const [isPending, startTransition] = useTransition();
const handleConcurrentClick = () => {
startTransition(() => {
setNumber((num) => num + 1)
})
}
let items = [];
for (let i = 0; i < 500; i++) {
items.push(<SlowPost key={i} data={number} />);
}
console.log('----组件渲染----')
return <div>
<button onClick={handleConcurrentClick}>触发更新</button>
<ul className="items">
{items}
</ul>
</div>
}
transition产生了有3次update任务
第一次
dispatchOptimisticSetState(fiber, false, queue, pendingState);
产生一次lane = 2的更新,isPending = true
第二次
var returnValue = callback();
回调函数的setState,这里lane = 256
第三次
dispatchSetState(fiber, queue, _entangledResult2);
产生一次lane = 256的更新,isPending = false
这三次update在processRootScheduleInMicrotask中把对应任务装入对应hook
然后进行render,此时lane = 2,进行同步渲染
此时只会进行lane = 2 的update,所以只会将isPending = true
之后commitRoot的时候还会触发一次processRootScheduleInMicrotask,这个时候进行下一轮的任务,lane = 256,进行shedule渲染
var newCallbackNode = scheduleCallback$2(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
等待schedule调度
var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && (!didTimeout);
var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);
没有过期任务,调度没有超时,进行renderRootConcurrent
renderRootConcurrent会进行workLoopConcurrent,和workLoopSync相比多了能截断
function workLoopConcurrent() {
// Perform work until Scheduler asks us to yield
while (workInProgress !== null && !shouldYield()) {
// $FlowFixMe[incompatible-call] found when upgrading Flow
performUnitOfWork(workInProgress);
}
}
shouldYield的逻辑是超出了5ms,返回true
如果截断后workInProgress !==null, 则说明render任务没有结束
后面又会注册processRootScheduleInMicrotask
ensureRootIsScheduled(root); // 注册processRootScheduleInMicrotask
return getContinuationForRoot(root, originalCallbackNode);
function getContinuationForRoot(root, originalCallbackNode) {
// This is called at the end of `performConcurrentWorkOnRoot` to determine
// if we need to schedule a continuation task.
//
// Usually `scheduleTaskForRootDuringMicrotask` only runs inside a microtask;
// however, since most of the logic for determining if we need a continuation
// versus a new task is the same, we cheat a bit and call it here. This is
// only safe to do because we know we're at the end of the browser task.
// So although it's not an actual microtask, it might as well be.
scheduleTaskForRootDuringMicrotask(root, now$1());
if (root.callbackNode === originalCallbackNode) {
// The task node scheduled for this root is the same one that's
// currently executed. Need to return a continuation.
return performConcurrentWorkOnRoot.bind(null, root);
}sheduleTask还没有执行完
return null;
}
同步getContinuationForRoot会进行一次shedule挂载,但是之前的sheduleTask还没有执行完,新的sheduleTask优先级和之前一样,所以保留之前的sheduleTask
之后返回performConcurrentWorkOnRoot给schedule,由于sheduleTask的执行结果还是个函数,shedule会在下一个宏任务继续执行这个函数,直到它执行完成,然后再去任务队列里面执行下一个任务,这也是concurrent模式下render fiber不会被其他任务打断的原因
如果是超时状态,那么shouldTimeSlice = false,会进行renderRootSync
schedule的优先级为3对应着5s,所以在这5s内的渲染都是以5ms为间隔划分,超时之后变为同步渲染
这张是触发isPending的同步渲染,可以看出task已经超时很久了
紧接着的是concurrennt渲染,每个task都超5ms一点点,也就是这个fiber到下一个fiber的时候会判断如果超了5ms就会推迟到下一个task