How to use commitLayoutEffects 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.

commitRoot.js

Source: commitRoot.js Github

copy
1import { ContentReset, Deletion, Placement, PlacementAndUpdate, Ref, Update } from "../reconciler/FiberFlags"
2
3
4
5let firstEffect = {
6  name: 'a',
7  nextEffect: {
8    name: 'b',
9    nextEffect: {
10      name: 'c',
11      nextEffect: null
12    }
13  }
14}
15
16
17const root = {
18  firstEffect
19}
20
21let nextEffect = null
22
23function commitRoot(root) {
24
25  let firstEffect = root.firstEffect
26
27  if (firstEffect !== null) {
28    console.log('has effect')
29
30    nextEffect = firstEffect
31    commitBeforeMutationEffects()
32
33    nextEffect = firstEffect
34    commitMutationEffects(root)
35
36    nextEffect = firstEffect
37    commitLayoutEffects(root)
38  }
39}
40
41function commitBeforeMutationEffects() {
42  console.log('function commit before mutation effects')
43
44  while(nextEffect !== null) {
45    nextEffect = nextEffect.nextEffect
46  }
47}
48
49function commitMutationEffects(root) {
50  console.log('function commit Mutation Effects')
51
52  while(nextEffect !== null) {
53    console.log(nextEffect.name)
54
55    const flags = nextEffect.flags
56
57    if (flags & ContentReset) {
58      // 重置 text 内容
59    }
60
61    if (flags & Ref) {
62      // 清空 ref
63    }
64
65    const primaryFlags = falgs & (Placement | Update | Deletion)
66    switch(primaryFlags) {
67      case PlacementAndUpdate:
68        commitPlacement(nextEffect)
69        nextEffect.flags &= ~Placement
70
71        const current = nextEffect.alternate
72        commitWork(current, nextEffect)
73        break
74    }
75    nextEffect = nextEffect.nextEffect
76  }
77}
78
79function commitLayoutEffects(root) {
80  console.log('function commit layout effects')
81}
82
83commitRoot(root)
84
85
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)