Skip to content

Commit ddf4443

Browse files
committed
render进一步解读,主要是currentTime、expirationTime、update相关概念,render的调用链请查看issue #2
1 parent 0af1847 commit ddf4443

File tree

5 files changed

+97
-12
lines changed

5 files changed

+97
-12
lines changed

packages/react-reconciler/src/ReactFiber.old.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,13 @@ function FiberNode(
122122
// child 指向子fiber(这里注意仅指向第一个子fiber而已)
123123
// sibling 指向下一个兄弟fiber,类比于链表中的next
124124
// index 表明当前fiber的索引
125-
// alternate 表示一个更新中的fiber
125+
// alternate 合并版本的fiber
126+
// 通常应用中会有两个fiber tree——old tree和workInProgress tree
127+
// old tree是已经渲染好的dom tree对应的fiber tree
128+
// workInProgress tree是正在执行更新中的fiber tree,还能实现中断恢复
129+
// 两颗树之间还是相互引用的,便于共享属性
130+
// 更新结束后,workInProgress tree就会变成old tree
131+
// 这样的做法称为 double buffering
126132
this.tag = tag;
127133
this.key = key;
128134
this.elementType = null;

packages/react-reconciler/src/ReactFiberExpirationTime.old.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,46 @@ const UNIT_SIZE = 10;
4242
const MAGIC_NUMBER_OFFSET = Batched - 1;
4343

4444
// 1 unit of expiration time represents 10ms.
45+
// 过期时间的一个单位是10ms
46+
// ——为啥要定义一个10ms的单位呢
47+
// ————若两次调用该函数的入参ms相差10ms以内,则返回相同的值
48+
// ————这意味着在调用层面是不关心10ms的误差的,只关心经过了几个过期时间单位
49+
// ————能通过这个实现节流,提高性能
50+
// 下面这个函数是用于计算1073741821与某个ms时间值/10(已经经过了几单位的过期时间)后的差值的
51+
// 如入参2515毫秒,先转成251个expiration time,| 0是为了取整用的
52+
// 然后继续计算1073741821 - 251并返回
53+
//
54+
// 这里可以理解为在单位刻度为10ms的时间轴上设了一个终点坐标MAGIC_NUMBER_OFFSET 1073741821
55+
// 每次调用这个函数会得到ms/10后离终点还有多少个单位,这个值越小,意味着ms越大
4556
export function msToExpirationTime(ms: number): ExpirationTime {
4657
// Always subtract from the offset so that we don't clash with the magic number for NoWork.
4758
return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
4859
}
4960

61+
// 与上面函数相反,用于从离终点刻度差值得到真正currentTime(当初调用now()得到的值)的
5062
export function expirationTimeToMs(expirationTime: ExpirationTime): number {
5163
return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
5264
}
5365

66+
/**
67+
* 按精度向上取整,如1101精度10,向上取整为1110
68+
*/
5469
function ceiling(num: number, precision: number): number {
5570
return (((num / precision) | 0) + 1) * precision;
5671
}
5772

73+
/**
74+
* 计算出不同优先级的到期时间
75+
* currentTime,当前时间相比MAGIC_NUMBER_OFFSET还有几个单位
76+
* expirationInMs,某类优先级的偏移时间
77+
* bucketSizeMs,步进时间,抹平一定的时间差,有点节流的意思
78+
* 假如这是一个交互任务
79+
* 比如说currentTime的值为1000,注意这个是“真正当前时间”与终点刻度的差值
80+
* expirationInMs为150,bucketSizeMs为100
81+
* 设MAGIC_NUMBER_OFFSET为C
82+
* C - ceiling(C - 1000 + 150/10, 100/10)
83+
* 得到的是一个比1000小15左右的值,离终点更近了15个UNIT_SIZE
84+
*/
5885
function computeExpirationBucket(
5986
currentTime,
6087
expirationInMs,

packages/react-reconciler/src/ReactFiberReconciler.old.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ export function updateContainer(
249249
if (__DEV__) {
250250
onScheduleRoot(container, element);
251251
}
252+
// 从FiberRoot中取出RootFiber
252253
const current = container.current;
254+
// 拿到当前时间
253255
const currentTime = requestCurrentTimeForUpdate();
254256
if (__DEV__) {
255257
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
@@ -259,6 +261,7 @@ export function updateContainer(
259261
}
260262
}
261263
const suspenseConfig = requestCurrentSuspenseConfig();
264+
// 计算过期时间expirationTime,数值越大优先级越高
262265
const expirationTime = computeExpirationForFiber(
263266
currentTime,
264267
current,
@@ -289,9 +292,12 @@ export function updateContainer(
289292
}
290293
}
291294

295+
// 创建update对象,这个对象和setState息息相关
296+
// 具体内部属性参见createUpdate
292297
const update = createUpdate(expirationTime, suspenseConfig);
293298
// Caution: React DevTools currently depends on this property
294299
// being called "element".
300+
// render其实也是一个更新,只不过没有setState,此时payload就给了个element
295301
update.payload = {element};
296302

297303
callback = callback === undefined ? null : callback;
@@ -305,10 +311,13 @@ export function updateContainer(
305311
);
306312
}
307313
}
314+
// 更新update.callback为ReactDOM.render传入的回调
308315
update.callback = callback;
309316
}
310317

318+
// 将update插入队列中
311319
enqueueUpdate(current, update);
320+
// 调度相关工作
312321
scheduleUpdateOnFiber(current, expirationTime);
313322

314323
return expirationTime;
@@ -437,7 +446,7 @@ export function findHostInstanceWithNoPortals(
437446
return hostFiber.stateNode;
438447
}
439448

440-
let shouldSuspendImpl = fiber => false;
449+
let shouldSuspendImpl = (fiber) => false;
441450

442451
export function shouldSuspend(fiber: Fiber): boolean {
443452
return shouldSuspendImpl(fiber);
@@ -516,7 +525,7 @@ if (__DEV__) {
516525
scheduleUpdateOnFiber(fiber, Sync);
517526
};
518527

519-
setSuspenseHandler = (newShouldSuspendImpl: Fiber => boolean) => {
528+
setSuspenseHandler = (newShouldSuspendImpl: (Fiber) => boolean) => {
520529
shouldSuspendImpl = newShouldSuspendImpl;
521530
};
522531
}

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,17 +308,34 @@ export function getWorkInProgressRoot(): FiberRoot | null {
308308
return workInProgressRoot;
309309
}
310310

311+
/**
312+
* 该函数只是拿到了一个react定义的currentTime,即大概相对于MAGIC_NUMBER_OFFSET过了多久了
313+
* 具体计算逻辑如下:
314+
* 初始化(包括重置了currentEventTime后)、Render、Commit,return msToExpirationTime(now());
315+
* 其他时候返回上次计算的currentEventTime
316+
*/
311317
export function requestCurrentTimeForUpdate() {
318+
// executionContext最开始的值为0b000000
319+
// RenderContext = 0b010000
320+
// CommitContext = 0b100000
321+
// 下面这个判断可以理解为通过executionContext控制当前是Render还是Commit阶段
322+
// 若是其中之一个阶段,就直接return msToExpirationTime
323+
// ReactDOM.render可以理解为初始化,并不属于Render或Commit阶段
312324
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
313325
// We're inside React, so it's fine to read the actual time.
314326
return msToExpirationTime(now());
315327
}
316328
// We're not inside React, so we may be in the middle of a browser event.
329+
// 既不是Render也不是Commit阶段,
330+
// 并且初始化后也没有调用performConcurrentWorkOnRoot重置currentEventTime
331+
// 就return 先前计算出来的currentEventTime
317332
if (currentEventTime !== NoWork) {
318333
// Use the same start time for all updates until we enter React again.
319334
return currentEventTime;
320335
}
321336
// This is the first update since React yielded. Compute a new start time.
337+
// 既不是Render也不是Commit阶段,
338+
// 初始化或调用performConcurrentWorkOnRoot重置了currentEventTime,同第一个判断的return
322339
currentEventTime = msToExpirationTime(now());
323340
return currentEventTime;
324341
}
@@ -327,30 +344,44 @@ export function getCurrentTime() {
327344
return msToExpirationTime(now());
328345
}
329346

347+
/**
348+
* 该函数用于计算过期时间的,该值越大,优先级越高,返回值有如下几种数字类型
349+
* Sync: 最大,优先级最高
350+
* Batched: Sync-1,优先级第二
351+
* renderExpirationTime: 要么为0,要么是之前已经用该函数计算出来的expirationTime
352+
* computeInteractiveExpiration: 比renderExpirationTime小一点
353+
* computeAsyncExpiration: 比computeInteractiveExpiration小一点
354+
*/
330355
export function computeExpirationForFiber(
331356
currentTime: ExpirationTime,
332357
fiber: Fiber,
333358
suspenseConfig: null | SuspenseConfig,
334359
): ExpirationTime {
335360
const mode = fiber.mode;
336361
if ((mode & BlockingMode) === NoMode) {
337-
return Sync;
362+
// BlockingMode=0010,那么只要mode的倒数第二位为0,就认为属于BlockingMode类别
363+
return Sync; // Sync,同步,数值最大,优先级最高
338364
}
339365

366+
// 获取当前调度的优先级,调度优先级分类见Scheduler.js
340367
const priorityLevel = getCurrentPriorityLevel();
341368
if ((mode & ConcurrentMode) === NoMode) {
369+
// ConcurrentMode=0100,那么只要mode的倒数第三位为0,就认为属于ConcurrentMode类别
370+
// Batched = Sync - 1,优先级第二
342371
return priorityLevel === ImmediatePriority ? Sync : Batched;
343372
}
344373

345374
if ((executionContext & RenderContext) !== NoContext) {
346375
// Use whatever time we're already rendering
347376
// TODO: Should there be a way to opt out, like with `runWithPriority`?
377+
// 若是Render阶段,就返回renderExpirationTime
348378
return renderExpirationTime;
349379
}
350380

351381
let expirationTime;
352382
if (suspenseConfig !== null) {
353383
// Compute an expiration time based on the Suspense timeout.
384+
// Suspense组件的特别的expirationTime,后面再研究
354385
expirationTime = computeSuspenseExpiration(
355386
currentTime,
356387
suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION,
@@ -363,11 +394,15 @@ export function computeExpirationForFiber(
363394
break;
364395
case UserBlockingPriority:
365396
// TODO: Rename this to computeUserBlockingExpiration
397+
// UserBlockingPriority,走交互过期时间
398+
// 这个可以理解为是发生用户交互事件的优先级处理
366399
expirationTime = computeInteractiveExpiration(currentTime);
367400
break;
368401
case NormalPriority:
369402
case LowPriority: // TODO: Handle LowPriority
370403
// TODO: Rename this to... something better.
404+
// NormaliPriority和LowPriority都是这个异步过期时间
405+
// 这个值会比交互过期时间的值还小一点
371406
expirationTime = computeAsyncExpiration(currentTime);
372407
break;
373408
case IdlePriority:
@@ -1099,7 +1134,7 @@ function flushPendingDiscreteUpdates() {
10991134
flushSyncCallbackQueue();
11001135
}
11011136

1102-
export function batchedUpdates<A, R>(fn: A => R, a: A): R {
1137+
export function batchedUpdates<A, R>(fn: (A) => R, a: A): R {
11031138
const prevExecutionContext = executionContext;
11041139
executionContext |= BatchedContext;
11051140
try {
@@ -1113,7 +1148,7 @@ export function batchedUpdates<A, R>(fn: A => R, a: A): R {
11131148
}
11141149
}
11151150

1116-
export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {
1151+
export function batchedEventUpdates<A, R>(fn: (A) => R, a: A): R {
11171152
const prevExecutionContext = executionContext;
11181153
executionContext |= EventContext;
11191154
try {
@@ -1163,7 +1198,7 @@ export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
11631198
}
11641199
}
11651200

1166-
export function flushSync<A, R>(fn: A => R, a: A): R {
1201+
export function flushSync<A, R>(fn: (A) => R, a: A): R {
11671202
const prevExecutionContext = executionContext;
11681203
if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {
11691204
if (__DEV__) {
@@ -3344,7 +3379,7 @@ function scheduleInteractions(root, expirationTime, interactions) {
33443379
const pendingInteractionMap = root.pendingInteractionMap_old;
33453380
const pendingInteractions = pendingInteractionMap.get(expirationTime);
33463381
if (pendingInteractions != null) {
3347-
interactions.forEach(interaction => {
3382+
interactions.forEach((interaction) => {
33483383
if (!pendingInteractions.has(interaction)) {
33493384
// Update the pending async work count for previously unscheduled interaction.
33503385
interaction.__count++;
@@ -3356,7 +3391,7 @@ function scheduleInteractions(root, expirationTime, interactions) {
33563391
pendingInteractionMap.set(expirationTime, new Set(interactions));
33573392

33583393
// Update the pending async work count for the current interactions.
3359-
interactions.forEach(interaction => {
3394+
interactions.forEach((interaction) => {
33603395
interaction.__count++;
33613396
});
33623397
}
@@ -3393,7 +3428,7 @@ function startWorkOnPendingInteractions(root, expirationTime) {
33933428
root.pendingInteractionMap_old.forEach(
33943429
(scheduledInteractions, scheduledExpirationTime) => {
33953430
if (scheduledExpirationTime >= expirationTime) {
3396-
scheduledInteractions.forEach(interaction =>
3431+
scheduledInteractions.forEach((interaction) =>
33973432
interactions.add(interaction),
33983433
);
33993434
}
@@ -3456,7 +3491,7 @@ function finishPendingInteractions(root, committedExpirationTime) {
34563491
if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
34573492
pendingInteractionMap.delete(scheduledExpirationTime);
34583493

3459-
scheduledInteractions.forEach(interaction => {
3494+
scheduledInteractions.forEach((interaction) => {
34603495
interaction.__count--;
34613496

34623497
if (subscriber !== null && interaction.__count === 0) {
@@ -3652,7 +3687,7 @@ export function act(callback: () => Thenable<mixed>): Thenable<void> {
36523687
}
36533688
});
36543689
},
3655-
err => {
3690+
(err) => {
36563691
onDone();
36573692
reject(err);
36583693
},

packages/react-reconciler/src/ReactUpdateQueue.old.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,17 @@ export function createUpdate(
195195
suspenseConfig,
196196

197197
tag: UpdateState,
198+
// setState的第一个参数
198199
payload: null,
200+
// 更新完的回调,1. ReactDOM.render中的回调 2. setState的回调
199201
callback: null,
200202

203+
// 用于在队列中找到下一个节点
204+
// 因为 update 其实就是一个队列中的节点,
205+
// 这个属性可以用于帮助我们寻找下一个 update
206+
// 对于批量更新来说,我们可能会创建多个 update
207+
// 因此我们需要将这些 update 串联并存储起来
208+
// 在必要的时候拿出来用于更新 state
201209
next: null,
202210
};
203211
if (__DEV__) {

0 commit comments

Comments
 (0)