|
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 |
|
| 17 | +import { WINDOW } from '../../types'; |
17 | 18 | import { bindReporter } from './lib/bindReporter'; |
18 | 19 | import { initMetric } from './lib/initMetric'; |
| 20 | +import { initUnique } from './lib/initUnique'; |
| 21 | +import { LayoutShiftManager } from './lib/LayoutShiftManager'; |
19 | 22 | import { observe } from './lib/observe'; |
20 | | -import { onHidden } from './lib/onHidden'; |
21 | 23 | import { runOnce } from './lib/runOnce'; |
22 | 24 | import { onFCP } from './onFCP'; |
23 | 25 | import type { CLSMetric, MetricRatingThresholds, ReportOpts } from './types'; |
@@ -54,58 +56,37 @@ export const onCLS = (onReport: (metric: CLSMetric) => void, opts: ReportOpts = |
54 | 56 | const metric = initMetric('CLS', 0); |
55 | 57 | let report: ReturnType<typeof bindReporter>; |
56 | 58 |
|
57 | | - let sessionValue = 0; |
58 | | - let sessionEntries: LayoutShift[] = []; |
| 59 | + const layoutShiftManager = initUnique(opts, LayoutShiftManager); |
59 | 60 |
|
60 | 61 | const handleEntries = (entries: LayoutShift[]) => { |
61 | | - entries.forEach(entry => { |
62 | | - // Only count layout shifts without recent user input. |
63 | | - if (!entry.hadRecentInput) { |
64 | | - const firstSessionEntry = sessionEntries[0]; |
65 | | - const lastSessionEntry = sessionEntries[sessionEntries.length - 1]; |
66 | | - |
67 | | - // If the entry occurred less than 1 second after the previous entry |
68 | | - // and less than 5 seconds after the first entry in the session, |
69 | | - // include the entry in the current session. Otherwise, start a new |
70 | | - // session. |
71 | | - if ( |
72 | | - sessionValue && |
73 | | - firstSessionEntry && |
74 | | - lastSessionEntry && |
75 | | - entry.startTime - lastSessionEntry.startTime < 1000 && |
76 | | - entry.startTime - firstSessionEntry.startTime < 5000 |
77 | | - ) { |
78 | | - sessionValue += entry.value; |
79 | | - sessionEntries.push(entry); |
80 | | - } else { |
81 | | - sessionValue = entry.value; |
82 | | - sessionEntries = [entry]; |
83 | | - } |
84 | | - } |
85 | | - }); |
| 62 | + for (const entry of entries) { |
| 63 | + layoutShiftManager._processEntry(entry); |
| 64 | + } |
86 | 65 |
|
87 | 66 | // If the current session value is larger than the current CLS value, |
88 | 67 | // update CLS and the entries contributing to it. |
89 | | - if (sessionValue > metric.value) { |
90 | | - metric.value = sessionValue; |
91 | | - metric.entries = sessionEntries; |
| 68 | + if (layoutShiftManager._sessionValue > metric.value) { |
| 69 | + metric.value = layoutShiftManager._sessionValue; |
| 70 | + metric.entries = layoutShiftManager._sessionEntries; |
92 | 71 | report(); |
93 | 72 | } |
94 | 73 | }; |
95 | 74 |
|
96 | 75 | const po = observe('layout-shift', handleEntries); |
97 | 76 | if (po) { |
98 | | - report = bindReporter(onReport, metric, CLSThresholds, opts.reportAllChanges); |
| 77 | + report = bindReporter(onReport, metric, CLSThresholds, opts!.reportAllChanges); |
99 | 78 |
|
100 | | - onHidden(() => { |
101 | | - handleEntries(po.takeRecords() as CLSMetric['entries']); |
102 | | - report(true); |
| 79 | + WINDOW.document?.addEventListener('visibilitychange', () => { |
| 80 | + if (WINDOW.document?.visibilityState === 'hidden') { |
| 81 | + handleEntries(po.takeRecords() as CLSMetric['entries']); |
| 82 | + report(true); |
| 83 | + } |
103 | 84 | }); |
104 | 85 |
|
105 | 86 | // Queue a task to report (if nothing else triggers a report first). |
106 | 87 | // This allows CLS to be reported as soon as FCP fires when |
107 | 88 | // `reportAllChanges` is true. |
108 | | - setTimeout(report, 0); |
| 89 | + WINDOW?.setTimeout?.(report); |
109 | 90 | } |
110 | 91 | }), |
111 | 92 | ); |
|
0 commit comments