performance-helper 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,12 @@ export declare class PerformanceCollector {
18
18
  * 采集所有性能指标
19
19
  */
20
20
  collect(): PerformanceMetrics;
21
+ /**
22
+ * 采集最终的 LCP 值
23
+ * 从 Performance API 中获取最新的 LCP entry,确保获取的是最终值
24
+ * 这与 Lighthouse 的计算方式一致
25
+ */
26
+ private collectFinalLCP;
21
27
  /**
22
28
  * 采集 Navigation Timing 指标
23
29
  * 使用 PerformanceNavigationTiming API(新标准)
@@ -39,6 +45,15 @@ export declare class PerformanceCollector {
39
45
  private collectWebVitals;
40
46
  /**
41
47
  * 观察 LCP
48
+ *
49
+ * 根据 Web Vitals 标准和 Lighthouse 的实现:
50
+ * - renderTime 和 loadTime 是相对于 performance.timeOrigin 的
51
+ * - timeOrigin 通常等于 navigation start
52
+ * - 所以 renderTime/loadTime/startTime 已经是相对于 navigation start 的
53
+ *
54
+ * Lighthouse 的计算方式:
55
+ * - 对于图片等资源:优先使用 renderTime,如果没有则使用 loadTime
56
+ * - 对于文本节点:使用 startTime
42
57
  */
43
58
  private observeLCP;
44
59
  /**
@@ -23,8 +23,52 @@ export class PerformanceCollector {
23
23
  */
24
24
  collect() {
25
25
  this.collectNavigationTiming();
26
+ // 重新获取最终的 LCP 值,确保与 Lighthouse 一致
27
+ // 因为 LCP 可能在页面加载过程中多次更新,需要在最终时刻获取
28
+ this.collectFinalLCP();
26
29
  return { ...this.metrics };
27
30
  }
31
+ /**
32
+ * 采集最终的 LCP 值
33
+ * 从 Performance API 中获取最新的 LCP entry,确保获取的是最终值
34
+ * 这与 Lighthouse 的计算方式一致
35
+ */
36
+ collectFinalLCP() {
37
+ if (!window.performance || !window.performance.getEntriesByType) {
38
+ return;
39
+ }
40
+ try {
41
+ // 获取所有 LCP entries,最后一个就是最终的 LCP
42
+ const lcpEntries = window.performance.getEntriesByType('largest-contentful-paint');
43
+ if (lcpEntries.length > 0) {
44
+ const lastEntry = lcpEntries[lcpEntries.length - 1];
45
+ // 使用与 observeLCP 相同的计算逻辑
46
+ let lcpValue;
47
+ if (lastEntry.renderTime !== undefined && lastEntry.renderTime !== null && lastEntry.renderTime > 0) {
48
+ lcpValue = lastEntry.renderTime;
49
+ }
50
+ else if (lastEntry.loadTime !== undefined && lastEntry.loadTime !== null && lastEntry.loadTime > 0) {
51
+ lcpValue = lastEntry.loadTime;
52
+ }
53
+ else {
54
+ lcpValue = lastEntry.startTime;
55
+ }
56
+ if (lcpValue !== undefined && lcpValue !== null && lcpValue > 0) {
57
+ this.metrics.lcp = Math.round(lcpValue);
58
+ // 更新 LCP 元素路径
59
+ if (lastEntry.element && lastEntry.element instanceof Element) {
60
+ const elementPath = getElementPath(lastEntry.element);
61
+ if (elementPath) {
62
+ this.metrics.lcpElement = elementPath;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ catch (e) {
69
+ // 浏览器不支持或出错,使用 observer 中已收集的值
70
+ }
71
+ }
28
72
  /**
29
73
  * 采集 Navigation Timing 指标
30
74
  * 使用 PerformanceNavigationTiming API(新标准)
@@ -136,6 +180,15 @@ export class PerformanceCollector {
136
180
  }
137
181
  /**
138
182
  * 观察 LCP
183
+ *
184
+ * 根据 Web Vitals 标准和 Lighthouse 的实现:
185
+ * - renderTime 和 loadTime 是相对于 performance.timeOrigin 的
186
+ * - timeOrigin 通常等于 navigation start
187
+ * - 所以 renderTime/loadTime/startTime 已经是相对于 navigation start 的
188
+ *
189
+ * Lighthouse 的计算方式:
190
+ * - 对于图片等资源:优先使用 renderTime,如果没有则使用 loadTime
191
+ * - 对于文本节点:使用 startTime
139
192
  */
140
193
  observeLCP() {
141
194
  if (!('PerformanceObserver' in window)) {
@@ -143,15 +196,32 @@ export class PerformanceCollector {
143
196
  }
144
197
  try {
145
198
  this.lcpObserver = new PerformanceObserver((list) => {
146
- var _a, _b;
147
199
  const entries = list.getEntries();
148
200
  const lastEntry = entries[entries.length - 1];
149
- // LCP 值计算优先级:
201
+ // LCP 值计算优先级(根据 Web Vitals 标准和 Lighthouse 实现):
150
202
  // 1. renderTime - 元素实际渲染到屏幕的时间(最准确,适用于图片等资源)
151
- // 2. startTime - PerformanceEntry 标准属性(总是存在,适用于文本节点等)
152
- // 3. loadTime - 资源加载完成时间(作为最后备选)
153
- const lcpValue = (_b = (_a = lastEntry.renderTime) !== null && _a !== void 0 ? _a : lastEntry.startTime) !== null && _b !== void 0 ? _b : lastEntry.loadTime;
154
- if (lcpValue) {
203
+ // 2. loadTime - 资源加载完成时间(适用于图片等资源,如果 renderTime 不可用)
204
+ // 3. startTime - PerformanceEntry 标准属性(总是存在,适用于文本节点等)
205
+ //
206
+ // 注意:这些时间值都是相对于 timeOrigin 的,而 timeOrigin 通常等于 navigation start
207
+ // 所以直接使用这些值即可,与 Lighthouse 的计算方式一致
208
+ let lcpValue;
209
+ if (lastEntry.renderTime !== undefined && lastEntry.renderTime !== null && lastEntry.renderTime > 0) {
210
+ // 优先使用 renderTime(图片等资源的实际渲染时间)
211
+ // 这是 Lighthouse 推荐的方式,最准确
212
+ lcpValue = lastEntry.renderTime;
213
+ }
214
+ else if (lastEntry.loadTime !== undefined && lastEntry.loadTime !== null && lastEntry.loadTime > 0) {
215
+ // 如果没有 renderTime,使用 loadTime(资源加载完成时间)
216
+ lcpValue = lastEntry.loadTime;
217
+ }
218
+ else {
219
+ // 最后使用 startTime(文本节点等)
220
+ lcpValue = lastEntry.startTime;
221
+ }
222
+ if (lcpValue !== undefined && lcpValue !== null && lcpValue > 0) {
223
+ // renderTime/loadTime/startTime 已经是相对于 timeOrigin 的
224
+ // 而 timeOrigin 通常等于 navigation start,所以直接使用即可
155
225
  this.metrics.lcp = Math.round(lcpValue);
156
226
  // 记录LCP元素的路径
157
227
  // LargestContentfulPaint entry 的 element 属性指向导致 LCP 的 DOM 元素
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "performance-helper",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "前端性能监控 SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",