How to use hasDiscreteLanes method in Playwright Internal

Best JavaScript code snippet using playwright-internal

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

before_mutation.js

Source: before_mutation.js Github

copy
1do {
2  // 触发useEffect回调与其他同步任务。由于这些任务可能触发新的渲染,所以这里要一直遍历执行直到没有任务
3  flushPassiveEffects();
4} while (rootWithPendingPassiveEffects !== null);
5
6// root指 fiberRootNode
7// root.finishedWork指当前应用的rootFiber
8const finishedWork = root.finishedWork;
9
10// 凡是变量名带lane的都是优先级相关
11const lanes = root.finishedLanes;
12if (finishedWork === null) {
13  return null;
14}
15root.finishedWork = null;
16root.finishedLanes = NoLanes;
17
18// 重置Scheduler绑定的回调函数
19root.callbackNode = null;
20root.callbackId = NoLanes;
21
22let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
23// 重置优先级相关变量
24markRootFinished(root, remainingLanes);
25
26// 清除已完成的discrete updates,例如:用户鼠标点击触发的更新。
27if (rootsWithPendingDiscreteUpdates !== null) {
28  if (!hasDiscreteLanes(remainingLanes) && rootsWithPendingDiscreteUpdates.has(root)) {
29    rootsWithPendingDiscreteUpdates.delete(root);
30  }
31}
32
33// 重置全局变量
34if (root === workInProgressRoot) {
35  workInProgressRoot = null;
36  workInProgress = null;
37  workInProgressRootRenderLanes = NoLanes;
38} else {
39}
40
41// 将effectList赋值给firstEffect
42// 由于每个fiber的effectList只包含他的子孙节点
43// 所以根节点如果有effectTag则不会被包含进来
44// 所以这里将有effectTag的根节点插入到effectList尾部
45// 这样才能保证有effect的fiber都在effectList中
46let firstEffect;
47if (finishedWork.effectTag > PerformedWork) {
48  if (finishedWork.lastEffect !== null) {
49    finishedWork.lastEffect.nextEffect = finishedWork;
50    firstEffect = finishedWork.firstEffect;
51  } else {
52    firstEffect = finishedWork;
53  }
54} else {
55  // 根节点没有effectTag
56  firstEffect = finishedWork.firstEffect;
57}
58
Full Screen

ReactFiberLane.js

Source: ReactFiberLane.js Github

copy
1import {
2  ImmediatePriority as ImmediateSchedulerPriority,
3  UserBlockingPriority as UserBlockingSchedulerPriority,
4  NormalPriority as NormalSchedulerPriority,
5  LowPriority as LowSchedulerPriority,
6  IdlePriority as IdleSchedulerPriority,
7  NoPriority as NoSchedulerPriority,
8} from './SchedulerWithReactIntegration';
9
10const SyncLanePriority = 15;
11const SyncBatchedLanePriority = 14;
12
13const InputDiscreteHydrationLanePriority = 13;
14const InputDiscreteLanePriority = 12;
15
16const InputContinuousHydrationLanePriority = 11;
17const InputContinuousLanePriority = 10;
18
19const DefaultHydrationLanePriority = 9;
20const DefaultLanePriority = 8;
21
22const TransitionHydrationPriority = 7;
23const TransitionPriority = 6;
24
25const RetryLanePriority = 5;
26
27const SelectiveHydrationLanePriority = 4;
28
29const IdleHydrationLanePriority = 3;
30const IdleLanePriority = 2;
31
32const OffscreenLanePriority = 1;
33
34const NoLanePriority = 0;
35
36const createLaneMap = (initial) =>
37  Array(31)
38    .fill(0)
39    .map(() => initial);
40
41const NoLanes = 0b0000000000000000000000000000000;
42const NoLane = 0b0000000000000000000000000000000;
43
44const SyncLane = 0b0000000000000000000000000000001;
45const SyncBatchedLane = 0b0000000000000000000000000000010;
46
47const InputDiscreteHydrationLane = 0b0000000000000000000000000000100;
48const InputDiscreteLanes = 0b0000000000000000000000000011000;
49
50const InputContinuousHydrationLane = 0b0000000000000000000000000100000;
51const InputContinuousLanes = 0b0000000000000000000000011000000;
52
53const DefaultHydrationLane = 0b0000000000000000000000100000000;
54const DefaultLanes = 0b0000000000000000000111000000000;
55
56const TransitionHydrationLane = 0b0000000000000000001000000000000;
57const TransitionLanes = 0b0000000001111111110000000000000;
58
59const IdleHydrationLane = 0b0001000000000000000000000000000;
60const IdleLanes = 0b0110000000000000000000000000000;
61const NonIdleLanes = 0b0000111111111111111111111111111;
62
63const OffscreenLane = 0b1000000000000000000000000000000;
64
65const NoTimestamp = -1;
66
67const getHighestPriorityLane = (lanes) => lanes & -lanes;
68
69const pickArbitraryLane = (lanes) => getHighestPriorityLane(lanes);
70
71const findUpdateLane = (lanePriority, wipLanes) => {
72  let lane;
73  switch (lanePriority) {
74    case NoLanePriority:
75      break;
76    case SyncLanePriority:
77      return SyncLane;
78    case SyncBatchedLanePriority:
79      return SyncBatchedLane;
80    case InputDiscreteLanePriority: {
81      lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);
82      if (lane === NoLane) {
83        return findUpdateLane(InputContinuousLanePriority, wipLanes);
84      }
85      return lane;
86    }
87    case InputContinuousLanePriority: {
88      lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);
89      if (lane === NoLane) {
90        return findUpdateLane(DefaultLanePriority, wipLanes);
91      }
92      return lane;
93    }
94    case DefaultLanePriority: {
95      lane = pickArbitraryLane(DefaultLanes & ~wipLanes);
96      if (lane === NoLane) {
97        lane = pickArbitraryLane(TransitionLanes & ~wipLanes);
98        if (lane === NoLane) {
99          lane = pickArbitraryLane(DefaultLanes);
100        }
101      }
102      return lane;
103    }
104    case TransitionPriority:
105    case RetryLanePriority:
106      break;
107    case IdleLanePriority:
108      lane = pickArbitraryLane(IdleLanes & ~wipLanes);
109      if (lane === NoLane) {
110        lane = pickArbitraryLane(IdleLanes);
111      }
112      return lane;
113    default:
114      break;
115  }
116
117  throw new Error('Invalid update priority: %s. This is a bug in React.');
118};
119
120const schedulerPriorityToLanePriority = (schedulerPriorityLevel) => {
121  switch (schedulerPriorityLevel) {
122    case ImmediateSchedulerPriority:
123      return SyncLanePriority;
124    case UserBlockingSchedulerPriority:
125      return InputContinuousLanePriority;
126    case NormalSchedulerPriority:
127    case LowSchedulerPriority:
128      return DefaultLanePriority;
129    case IdleSchedulerPriority:
130      return IdleLanePriority;
131    default:
132      return NoLanePriority;
133  }
134};
135
136const isSubsetOfLanes = (set, subset) => (set & subset) === subset;
137
138const mergeLanes = (a, b) => a | b;
139
140const pickArbitraryLaneIndex = (lane) => 31 - Math.clz32(lane);
141
142const markRootUpdated = (root, updateLane, eventTime) => {
143  root.pendingLanes |= updateLane;
144
145  const higherPriorityLanes = updateLane - 1;
146
147  root.suspendedLanes &= higherPriorityLanes;
148  root.pingedLanes &= higherPriorityLanes;
149
150  const eventTimes = root.eventTimes;
151  const index = pickArbitraryLaneIndex(updateLane);
152
153  eventTimes[index] = eventTime;
154};
155
156const markRootSuspended = (root, suspendedLanes) => {
157  root.suspendedLanes |= suspendedLanes;
158  root.pingedLanes &= ~suspendedLanes;
159
160  const expirationTimes = root.expirationTimes;
161
162  let lanes = suspendedLanes;
163  while (lanes > 0) {
164    const index = pickArbitraryLaneIndex(lanes);
165    const lane = 1 << index;
166
167    expirationTimes[index] = NoTimestamp;
168
169    lanes &= ~lane;
170  }
171};
172
173const includesSomeLane = (a, b) => (a & b) !== NoLanes;
174
175let return_highestLanePriority = DefaultLanePriority;
176
177const getHighestPriorityLanes = (lanes) => {
178  if ((SyncLane & lanes) !== NoLanes) {
179    return_highestLanePriority = SyncLanePriority;
180    return SyncLane;
181  }
182  if ((SyncBatchedLane & lanes) !== NoLanes) {
183    return_highestLanePriority = SyncBatchedLanePriority;
184    return SyncBatchedLane;
185  }
186  if ((InputDiscreteHydrationLane & lanes) !== NoLanes) {
187    return_highestLanePriority = InputDiscreteHydrationLanePriority;
188    return InputDiscreteHydrationLane;
189  }
190  const inputDiscreteLanes = InputDiscreteLanes & lanes;
191  if (inputDiscreteLanes !== NoLanes) {
192    return_highestLanePriority = InputDiscreteLanePriority;
193    return inputDiscreteLanes;
194  }
195  if ((lanes & InputContinuousHydrationLane) !== NoLanes) {
196    return_highestLanePriority = InputContinuousHydrationLanePriority;
197    return InputContinuousHydrationLane;
198  }
199  const inputContinuousLanes = InputContinuousLanes & lanes;
200  if (inputContinuousLanes !== NoLanes) {
201    return_highestLanePriority = InputContinuousLanePriority;
202    return inputContinuousLanes;
203  }
204  if ((lanes & DefaultHydrationLane) !== NoLanes) {
205    return_highestLanePriority = DefaultHydrationLanePriority;
206    return DefaultHydrationLane;
207  }
208  const defaultLanes = DefaultLanes & lanes;
209  if (defaultLanes !== NoLanes) {
210    return_highestLanePriority = DefaultLanePriority;
211    return defaultLanes;
212  }
213  if ((lanes & TransitionHydrationLane) !== NoLanes) {
214    return_highestLanePriority = TransitionHydrationPriority;
215    return TransitionHydrationLane;
216  }
217  const transitionLanes = TransitionLanes & lanes;
218  if (transitionLanes !== NoLanes) {
219    return_highestLanePriority = TransitionPriority;
220    return transitionLanes;
221  }
222  const retryLanes = RetryLanes & lanes;
223  if (retryLanes !== NoLanes) {
224    return_highestLanePriority = RetryLanePriority;
225    return retryLanes;
226  }
227  if (lanes & SelectiveHydrationLane) {
228    return_highestLanePriority = SelectiveHydrationLanePriority;
229    return SelectiveHydrationLane;
230  }
231  if ((lanes & IdleHydrationLane) !== NoLanes) {
232    return_highestLanePriority = IdleHydrationLanePriority;
233    return IdleHydrationLane;
234  }
235  const idleLanes = IdleLanes & lanes;
236  if (idleLanes !== NoLanes) {
237    return_highestLanePriority = IdleLanePriority;
238    return idleLanes;
239  }
240  if ((OffscreenLane & lanes) !== NoLanes) {
241    return_highestLanePriority = OffscreenLanePriority;
242    return OffscreenLane;
243  }
244
245  return_highestLanePriority = DefaultLanePriority;
246  return lanes;
247};
248
249const getLowestPriorityLane = (lanes) => {
250  const index = 31 - Math.clz32(lanes);
251  return index < 0 ? NoLanes : 1 << index;
252};
253
254const getNextLanes = (root, wipLanes) => {
255  const pendingLanes = root.pendingLanes;
256  if (pendingLanes === NoLanes) {
257    return_highestLanePriority = NoLanePriority;
258    return NoLanes;
259  }
260
261  let nextLanes = NoLanes;
262  let nextLanePriority = NoLanePriority;
263
264  const expiredLanes = root.expiredLanes;
265  const suspendedLanes = root.suspendedLanes;
266  const pingedLanes = root.pingedLanes;
267
268  if (expiredLanes !== NoLanes) {
269    nextLanes = expiredLanes;
270    nextLanePriority = return_highestLanePriority = SyncLanePriority;
271  } else {
272    const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
273    if (nonIdlePendingLanes !== NoLanes) {
274      const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
275      if (nonIdleUnblockedLanes !== NoLanes) {
276        nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
277        nextLanePriority = return_highestLanePriority;
278      } else {
279        const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
280        if (nonIdlePingedLanes !== NoLanes) {
281          nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
282          nextLanePriority = return_highestLanePriority;
283        }
284      }
285    } else {
286      const unblockedLanes = pendingLanes & ~suspendedLanes;
287      if (unblockedLanes !== NoLanes) {
288        nextLanes = getHighestPriorityLanes(unblockedLanes);
289        nextLanePriority = return_highestLanePriority;
290      } else {
291        if (pingedLanes !== NoLanes) {
292          nextLanes = getHighestPriorityLanes(pingedLanes);
293          nextLanePriority = return_highestLanePriority;
294        }
295      }
296    }
297  }
298
299  if (nextLanes === NoLanes) {
300    return NoLanes;
301  }
302
303  nextLanes = pendingLanes & ((getLowestPriorityLane(nextLanes) << 1) - 1);
304
305  if (
306    wipLanes !== NoLanes &&
307    wipLanes !== nextLanes &&
308    (wipLanes & suspendedLanes) === NoLanes
309  ) {
310    getHighestPriorityLanes(wipLanes);
311    const wipLanePriority = return_highestLanePriority;
312    if (nextLanePriority <= wipLanePriority) {
313      return wipLanes;
314    } else {
315      return_highestLanePriority = nextLanePriority;
316    }
317  }
318
319  const entangledLanes = root.entangledLanes;
320  if (entangledLanes !== NoLanes) {
321    const entanglements = root.entanglements;
322    let lanes = nextLanes & entangledLanes;
323    while (lanes > 0) {
324      const index = pickArbitraryLaneIndex(lanes);
325      const lane = 1 << index;
326
327      nextLanes |= entanglements[index];
328
329      lanes &= ~lane;
330    }
331  }
332
333  return nextLanes;
334};
335
336const markRootFinished = (root, remainingLanes) => {
337  const noLongerPendingLanes = root.pendingLanes & ~remainingLanes;
338
339  root.pendingLanes = remainingLanes;
340
341  root.suspendedLanes = 0;
342  root.pingedLanes = 0;
343
344  root.expiredLanes &= remainingLanes;
345  root.mutableReadLanes &= remainingLanes;
346
347  root.entangledLanes &= remainingLanes;
348
349  const entanglements = root.entanglements;
350  const eventTimes = root.eventTimes;
351  const expirationTimes = root.expirationTimes;
352
353  let lanes = noLongerPendingLanes;
354  while (lanes > 0) {
355    const index = pickArbitraryLaneIndex(lanes);
356    const lane = 1 << index;
357
358    entanglements[index] = NoLanes;
359    eventTimes[index] = NoTimestamp;
360    expirationTimes[index] = NoTimestamp;
361
362    lanes &= ~lane;
363  }
364};
365
366const hasDiscreteLanes = (lanes) => (lanes & InputDiscreteLanes) !== NoLanes;
367
368const computeExpirationTime = (lane, currentTime) => {
369  getHighestPriorityLanes(lane);
370  const priority = return_highestLanePriority;
371  if (priority >= InputContinuousLanePriority) {
372    return currentTime + 250;
373  } else if (priority >= TransitionPriority) {
374    return currentTime + 5000;
375  } else {
376    return NoTimestamp;
377  }
378};
379
380const markStarvedLanesAsExpired = (root, currentTime) => {
381  const pendingLanes = root.pendingLanes;
382  const suspendedLanes = root.suspendedLanes;
383  const pingedLanes = root.pingedLanes;
384  const expirationTimes = root.expirationTimes;
385
386  let lanes = pendingLanes;
387  while (lanes > 0) {
388    const index = pickArbitraryLaneIndex(lanes);
389    const lane = 1 << index;
390
391    const expirationTime = expirationTimes[index];
392    if (expirationTime === NoTimestamp) {
393      if (
394        (lane & suspendedLanes) === NoLanes ||
395        (lane & pingedLanes) !== NoLanes
396      ) {
397        expirationTimes[index] = computeExpirationTime(lane, currentTime);
398      }
399    } else if (expirationTime <= currentTime) {
400      root.expiredLanes |= lane;
401    }
402
403    lanes &= ~lane;
404  }
405};
406
407const returnNextLanesPriority = () => return_highestLanePriority;
408
409const lanePriorityToSchedulerPriority = (lanePriority) => {
410  switch (lanePriority) {
411    case SyncLanePriority:
412    case SyncBatchedLanePriority:
413      return ImmediateSchedulerPriority;
414    case InputDiscreteHydrationLanePriority:
415    case InputDiscreteLanePriority:
416    case InputContinuousHydrationLanePriority:
417    case InputContinuousLanePriority:
418      return UserBlockingSchedulerPriority;
419    case DefaultHydrationLanePriority:
420    case DefaultLanePriority:
421    case TransitionHydrationPriority:
422    case TransitionPriority:
423    case SelectiveHydrationLanePriority:
424    case RetryLanePriority:
425      return NormalSchedulerPriority;
426    case IdleHydrationLanePriority:
427    case IdleLanePriority:
428    case OffscreenLanePriority:
429      return IdleSchedulerPriority;
430    case NoLanePriority:
431      return NoSchedulerPriority;
432    default:
433      invariant(
434        false,
435        'Invalid update priority: %s. This is a bug in React.',
436        lanePriority
437      );
438  }
439};
440
441export {
442  SyncLanePriority,
443  SyncBatchedLanePriority,
444  InputDiscreteLanePriority,
445  InputContinuousLanePriority,
446  DefaultLanePriority,
447  TransitionPriority,
448  NoLanePriority,
449  createLaneMap,
450  NoLanes,
451  NoLane,
452  SyncLane,
453  SyncBatchedLane,
454  InputDiscreteHydrationLane,
455  DefaultHydrationLane,
456  DefaultLanes,
457  IdleHydrationLane,
458  OffscreenLane,
459  NoTimestamp,
460  pickArbitraryLane,
461  findUpdateLane,
462  schedulerPriorityToLanePriority,
463  isSubsetOfLanes,
464  mergeLanes,
465  markRootUpdated,
466  markRootSuspended,
467  includesSomeLane,
468  getNextLanes,
469  markRootFinished,
470  hasDiscreteLanes,
471  markStarvedLanesAsExpired,
472  returnNextLanesPriority,
473  lanePriorityToSchedulerPriority,
474};
475
Full Screen

renderer.js

Source: renderer.js Github

copy
1// commitRootImpl方法
2
3// before mutation之前
4
5do {
6    // 触发useEffect回调与其他同步任务。由于这些任务可能触发新的渲染,所以这里要一直遍历执行直到没有任务
7    flushPassiveEffects();
8  } while (rootWithPendingPassiveEffects !== null);
9
10  // root指 fiberRootNode
11  // root.finishedWork指当前应用的rootFiber? TODO
12  const finishedWork = root.finishedWork;
13
14  // 凡是变量名带lane的都是优先级相关
15  const lanes = root.finishedLanes;
16  if (finishedWork === null) {
17    return null;
18  }
19  root.finishedWork = null;
20  root.finishedLanes = NoLanes;
21
22  // 重置Scheduler绑定的回调函数
23  root.callbackNode = null;
24  root.callbackId = NoLanes;
25
26  let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
27  // 重置优先级相关变量
28  markRootFinished(root, remainingLanes);
29
30  // 清除已完成的discrete updates,例如:用户鼠标点击触发的更新。
31  if (rootsWithPendingDiscreteUpdates !== null) {
32    if (
33      !hasDiscreteLanes(remainingLanes) &&
34      rootsWithPendingDiscreteUpdates.has(root)
35    ) {
36      rootsWithPendingDiscreteUpdates.delete(root);
37    }
38  }
39
40  // 重置全局变量
41  if (root === workInProgressRoot) {
42    workInProgressRoot = null;
43    workInProgress = null;
44    workInProgressRootRenderLanes = NoLanes;
45  } else {
46  }
47
48  // 将effectList赋值给firstEffect
49  // 由于每个fiber的effectList只包含他的子孙节点 TODO
50  // 所以根节点如果有effectTag则不会被包含进来
51  // 所以这里将有effectTag的根节点插入到effectList尾部
52  // 这样才能保证有effect的fiber都在effectList中
53  let firstEffect;
54  // 根节点有effect的情况
55  if (finishedWork.effectTag > PerformedWork) {
56    if (finishedWork.lastEffect !== null) {
57        //如果已经存在lastEffect,则把根节点放在effect链的最后
58      finishedWork.lastEffect.nextEffect = finishedWork;
59      firstEffect = finishedWork.firstEffect;
60    } else {
61        // 如果没有lastEffect,则把根节点放在effect链的最前
62      firstEffect = finishedWork;
63    }
64  } else {
65    // 根节点没有effectTag
66    firstEffect = finishedWork.firstEffect;
67  }
68
69
70  // layout阶段之后
71  /**
72   * 主要
73   */
74
75  const rootDidHavePassiveEffects = rootDoseHavePassiveEffects;
76
77  // useEffect相关
78  if(rootDoseHavePassiveEffects){
79    rootDoseHavePassiveEffects = false;
80    rootWithPendingPassiveEffects = root;
81    pendingPassiveEffectsLanes = lanes;
82    pendingPassiveEffectsRenderPriority = renderPriorityLevel;
83  } else {}
84
85  // 性能优化相关
86  if(remainingLanes !== NoLanes) {
87    if(enableSchedulerTracing){
88
89    }
90  } else {
91
92  }
93
94  if(enableSchedulerTracing){
95    if(!rootDidHavePassiveEffects){
96
97    }
98  }
99
100  // 检查无限循环的同步任务
101  if(ramainingLanes === SyncLane){
102
103  }
104
105  // 在离开commitRoot函数前调用, 触发一次新的调度,确保任何附加的任务被调度
106  ensureRootIsScheduled(root, now());
107
108  // ...处理未捕获错误及老版本遗留的边界问题
109
110  // 执行同步任务,这样同步任务不需要等到下次事件循环在执行
111  // 比如componentDidxxx中执行setState,useLayoutEffect中创建的更新会在这里被同步执行
112  flushSyncCallbackQueue();
113
114  return null;
115
116  // before mutation 阶段
117  // 重点在下面这个函数
118  function commitBeforeMutationEffects(finishedWork){
119    while (nextEffect !== null) {
120      const current = nextEffect.alternate;
121
122      if(!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null){
123        // focus blur相关
124      }
125
126      const effectTag = nextEffect.effectTag
127
128      // 调用getSnapshotBeforeUpdate
129      if((effectTag & Snapshot) !== NoEffect) {
130        commitBeforeMutationEffectsOnFiber(current, nextEffect);
131      }
132
133      // 调用useEffect
134      if((effectTag & Passive) !== NoEffect) {
135        if(!rootDoseHavePassiveEffects) {
136          rootDoseHavePassiveEffects = true;
137          // 由Schedule模块提供,用于以某个优先级异步调度一个回调函数
138          scheduleCallback(NormalSchedulerPriority, () => {
139            // 触发useEffect
140            flushPassiveEffects();
141            return null;
142          });
143        }
144      }
145      nextEffect = nextEffect.nextEffect
146    }
147  }
148
149  // mutation 阶段
150
151  nextEffect = firstEffect;
152  do{
153    try{
154      commitMutationEffects(root, renderPriorityLevel);
155    } catch(error) {
156      invariant(nextEffect !== null, 'Should be working on an effect.');
157      captureCommitPhaseError(nextEffect, error);
158      nextEffect = nextEffect.nextEffect;
159    }
160  } while (nextEffect !== null)
161
162  // mutation阶段核心
163  function commitMutationEffects(){
164    root: FiberRoot, 
165    renderPriorityLevel
166  } {
167    // 遍历effectList
168    while(nextEffect !== null) {
169      const effectTag = nextEffect.effectTag;
170
171      // 根据ContentReset effectTag重置文字节点
172      if(effectTag & ContentReset){
173        commitResetTextContent(nextEffect);
174      }
175
176      // 更新ref
177      if(effectTag & Ref){
178        const current = nextEffect.alternate;
179        if(current !== null){
180          commitDetachRef(current);
181        }
182      }
183
184      // 根据effectTag分别处理
185      const primaryEffectTag = effectTag & (Placement | Update | Deletion | Hydrating)
186
187      switch (primaryEffectTag) {
188        // 插入DOM
189        case Placement: {
190          commitPlacement(nextEffect);
191          nextEffect.effectTag &= ~Placement; // 记录执行后的状态
192          break;
193        }
194        // 插入DOM并更新DOM
195        case PlacementAndUpdate: {
196          commitPlacement(nextEffect);
197
198          nextEffect.effectTag &= ~Placement;
199
200          // 更新DOM
201          const current = nextEffect.alternate;
202          commitWork(current, nextEffect);
203          break;
204        }
205
206        // SSR相关操作 ...略
207
208        case Update: {
209          // 更新DOM
210          const current = nextEffect.alternate;
211          commitWork(current, nextEffect);
212          break;
213        }
214
215        case Deletion: {
216          // 删除DOM
217          commitDeletion(root, nextEffect, renderPriortyLevel);
218          break;
219        }
220      }
221
222      nextEffect = nextEffect.nextEffect;
223    }
224  }
225
226  function commitPlacement(nextEffect){
227    // 获取父级DOM节点。其中finishedWork为传入的Fiber节点
228    const parentFiber = getHostParentFiber(finishedWork);
229    // 父级DOM节点
230    const parentStateNode = parentFiber.stateNode;
231
232    // 获取Fiber节点的DOM兄弟节点
233    const before = getHostSibling(finishedWork);
234
235    // 根据兄弟节点是否存在决定调用 parentNode.insertBefore 或 parentNode.appendChild执行DOM插入操作
236    if(isContainer){
237      insertOrAppendPlacementNodeIntoContainer(finishedWork, before, parent);
238    } else {
239      insertOrAppendPlacementNode(finishedWork, before, parent)
240    }
241  }
242
243  function commitUpdate() {
244    // 根据Fiber.Tag分别处理
245
246    // tag为FunctionComponent的情况
247    // 该方法会遍历effectList 执行所有useLayoutEffect hook的销毁函数
248    commitHookEffectListUnmonut()
249
250    // tag为HostComponent的情况
251    for(let i = 0; i < updatePayload.length; i += 2){
252      const propKey = updatePayload[i];
253      const propValue = updatePayload[i + 1];
254
255      // 处理 style
256      if(propKey === STYLE){
257        setValueForStyles(domElement, propValue);
258      } else if (propKey === DANGEROUSLY_SET_INNER_HTML){
259        setInnerHtml(domElement, propValue);
260      } else if (propKey === CHILDREN) {
261        setTextContent(domElement, propValue)
262      } else {
263        // 处理剩余props
264        setValueForProperty(domElement, propKey, propValuem, isCustomComponentTag);
265      }
266    }
267  }
268
269  function commitDeletion() {
270    /**
271     * 递归调用ClassComponent的componentWillUnmount,从页面移除DOM
272     * 解绑ref
273     * 调度useEffect的销毁函数
274     */
275  }
276
277
278  // layout 阶段
279  /**
280   * 这个阶段是在DOM已经渲染完成后执行的,在这个阶段触发的生命周期和hook可以直接访问已改变的DOM
281   */
282
283   root.current = finishedWork;
284   nextEffect = firstEffect;
285   do{
286     try{
287       commitLayoutEffects(root, lanes);
288     } catch(error) {
289       invariant(nextEffect !== null, "Should be working on an effect.");
290       captureCommitPhaseError(nextEffect, error);
291       nextEffect = nextEffect.nextEffect;
292     }
293   } while (nextEffect !== null);
294
295   nextEffect = null;
296
297  function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes){
298    while(nextEffect !== null){
299      const effectTag = nextEffect.effectTag;
300
301      // 调用生命周期钩子和hook
302      if(effectTag & (Update | Callback)){
303        const current = nextEffect.alternate;
304        commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
305      }
306
307      // 赋值ref
308      if(effectTag & Ref) {
309        commitAttachRef(nextEffect);
310      }
311
312      nextEffect = nextEffect.nextEffect
313    }
314  }
315
316  function commitLayoutEffectOnFiber() {
317    // 根据fiber.tag对不同类型节点分别处理
318    // 对于ClassComponent, 会通过current === null 区分是mount还是update,调用componentDidMount 或componentDidUpdate
319    // 触发状态更新的this.setState如果赋值了第二个参数回调函数,也会在此时调用。
320
321    // 对于FunctionCompnent及相关类型
322    switch (finishedWork.tag) {
323      // 以下都是FunctionComponent及相关类型
324      case FunctionComponent:
325      case ForwardRef:
326      case SimpleMemoComponent:
327      case Block: {
328        // 执行useLayoutEffect的回调函数
329        commmitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
330        // 调度useEffect的销毁函数与回调函数
331        schedulePassiveEffects(finishedWork);
332        return;
333      }
334    }
335  }
336
337  // 深入flushPassiveEffects (中文意思为:清洗被动效果)
338  // flushPassiveEffects内部会设置优先级,并执行flushPassiveEffectsImpl,Impl的意思是实现,即implementation的简写
339  /**
340   * flushPassiveEffectsImpl主要做三件事
341   * 1. 调用该useEffect在上一次render时的销毁函数
342   * 2. 调用该useEffect在本次render时的回调函数
343   * 3. 如果存在同步任务,不需要等待下次事件循环的宏任务,提前执行
344   * 1,2两步会在layout阶段之后异步执行
345   */
346
347// 阶段一:销毁函数的执行
348/**
349 * useEffect会保证在执行完全部的销毁函数之后,再执行回调函数
350 */
351
352// pendingPassiveHookEffectsUnmount中保存了所有需要执行销毁的useEffect
353const unmountEffects = pendingPassiveHookEffectsUnmount;
354pendingPassiveHookEffectsUnmount = [];
355for (let i = 0; i < unmountEffects.length; i += 2) {
356  const effect = ((unmountEffects[i]: any): HookEffect);
357  const fiber = ((unmountEffects[i + 1]: any): Fiber);
358  const distroy = effect.distroy;
359  effect.distroy = undefined;
360
361  if(typeof destroy == 'function') {
362    // 销毁函数存在则执行
363    try {
364      destroy();
365    } catch (error) {
366      captureCommitPhaseError(fiber, error);
367    }
368  }
369}
370
371
372function schedulePassiveEffects(finishedWork: Fiber) {
373  const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
374  const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
375
376  if(lastEffect !== null) {
377    const firstEffect = lastEffect.next;
378    let effect = firstEffect;
379
380    do {
381      const { next, tag } = effect;
382      if(
383        (tag & HookPassive) !== NoHookEffect &&
384        (tag & HookHasEffect) !== NoHookEffect
385       ) {
386         // 向pendingPassiveHookEffectsUnmount数组内push要销毁的effect
387         enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
388         // 向pendingPassiveHookEffectMount数组内push要执行回调的effect
389         enqueuePendingPassiveHookEffectMount(finishedWork, effect);
390       }
391       effect = next;
392    } while ( effect !== firstEffect );
393  }
394}
395
396// 阶段二: 回调函数的执行
397const mountEffects = pendingPassiveHookEffectsMount;
398pendingPassiveHookEffectsMount = [];
399for(let i = 0; i < mountEffects.length; i += 2) {
400  const effect = ((mountEffects[i]: any): HookEffect);
401  const fiber = ((mountEffects[i + 1]: any): Fiber);
402
403  try {
404    const create = effect.create;
405    effect.distroy = create();
406  } catch (error) {
407    captureCommitPhaseError(fiber, error);
408  }
409}
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Playwright Internal on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)