@be-link/cls-logger 1.0.1-beta.20 → 1.0.1-beta.21

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.
package/dist/index.esm.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { onFCP, onLCP, onCLS, onINP, onTTFB } from 'web-vitals';
2
+
1
3
  function isPlainObject(value) {
2
4
  return Object.prototype.toString.call(value) === '[object Object]';
3
5
  }
@@ -1958,14 +1960,10 @@ function getPagePath() {
1958
1960
  return '';
1959
1961
  }
1960
1962
  }
1961
- function reportMetric(report, reportType, metric, value, extra = {}) {
1962
- report(reportType, {
1963
- pagePath: getPagePath(),
1964
- metric,
1965
- value,
1966
- ...extra,
1967
- });
1968
- }
1963
+ /**
1964
+ * 安装浏览器性能监控
1965
+ * 使用 Google web-vitals 库实现 Core Web Vitals 指标采集
1966
+ */
1969
1967
  function installBrowserPerformanceMonitor(report, options) {
1970
1968
  if (typeof window === 'undefined')
1971
1969
  return;
@@ -1974,145 +1972,100 @@ function installBrowserPerformanceMonitor(report, options) {
1974
1972
  return;
1975
1973
  w.__beLinkClsLoggerPerfInstalled__ = true;
1976
1974
  const ignoreUrls = [...DEFAULT_IGNORE, ...(options.ignoreUrls ?? [])];
1977
- // Navigation timing: TTFB
1978
- if (options.navigationTiming) {
1975
+ // Web Vitals: 使用 Google web-vitals 库
1976
+ // 这个库会自动处理:
1977
+ // - LCP 在用户交互后停止观察
1978
+ // - firstHiddenTime 过滤(页面隐藏后的数据不计入)
1979
+ // - CLS 5秒会话窗口算法
1980
+ // - BFCache 恢复时自动重置指标
1981
+ if (options.webVitals) {
1982
+ // FCP
1979
1983
  try {
1980
- const navEntries = performance.getEntriesByType?.('navigation');
1981
- const nav = Array.isArray(navEntries) ? navEntries[0] : undefined;
1982
- if (nav && typeof nav === 'object') {
1983
- const ttfb = typeof nav.responseStart === 'number' && typeof nav.requestStart === 'number'
1984
- ? nav.responseStart - nav.requestStart
1985
- : -1;
1986
- if (ttfb >= 0 && sampleHit$1(options.sampleRate))
1987
- reportMetric(report, options.reportType, 'TTFB', ttfb, { unit: 'ms' });
1988
- }
1984
+ onFCP((metric) => {
1985
+ report(options.reportType, {
1986
+ pagePath: getPagePath(),
1987
+ metric: 'FCP',
1988
+ value: metric.value,
1989
+ unit: 'ms',
1990
+ rating: metric.rating,
1991
+ id: metric.id,
1992
+ navigationType: metric.navigationType,
1993
+ });
1994
+ });
1989
1995
  }
1990
1996
  catch {
1991
1997
  // ignore
1992
1998
  }
1993
- }
1994
- // Web Vitals: FCP/LCP/CLS/FID
1995
- if (options.webVitals && typeof globalThis.PerformanceObserver === 'function') {
1996
- // FCP
1999
+ // LCP(自动处理用户交互后停止)
1997
2000
  try {
1998
- const po = new PerformanceObserver((list) => {
1999
- try {
2000
- if (!sampleHit$1(options.sampleRate))
2001
- return;
2002
- for (const entry of list.getEntries()) {
2003
- if (entry?.name === 'first-contentful-paint' && typeof entry.startTime === 'number') {
2004
- reportMetric(report, options.reportType, 'FCP', entry.startTime, { unit: 'ms' });
2005
- }
2006
- }
2007
- }
2008
- catch {
2009
- // ignore
2010
- }
2001
+ onLCP((metric) => {
2002
+ report(options.reportType, {
2003
+ pagePath: getPagePath(),
2004
+ metric: 'LCP',
2005
+ value: metric.value,
2006
+ unit: 'ms',
2007
+ rating: metric.rating,
2008
+ id: metric.id,
2009
+ navigationType: metric.navigationType,
2010
+ });
2011
2011
  });
2012
- po.observe({ type: 'paint', buffered: true });
2013
2012
  }
2014
2013
  catch {
2015
2014
  // ignore
2016
2015
  }
2017
- // LCP(最后一次为准)
2018
- let lastLcp = null;
2016
+ // CLS(自动处理会话窗口)
2019
2017
  try {
2020
- const po = new PerformanceObserver((list) => {
2021
- try {
2022
- const entries = list.getEntries();
2023
- if (entries && entries.length)
2024
- lastLcp = entries[entries.length - 1];
2025
- }
2026
- catch {
2027
- // ignore
2028
- }
2018
+ onCLS((metric) => {
2019
+ report(options.reportType, {
2020
+ pagePath: getPagePath(),
2021
+ metric: 'CLS',
2022
+ value: metric.value,
2023
+ unit: 'score',
2024
+ rating: metric.rating,
2025
+ id: metric.id,
2026
+ navigationType: metric.navigationType,
2027
+ });
2029
2028
  });
2030
- po.observe({ type: 'largest-contentful-paint', buffered: true });
2031
- const flushLcp = () => {
2032
- try {
2033
- if (!lastLcp)
2034
- return;
2035
- if (!sampleHit$1(options.sampleRate))
2036
- return;
2037
- if (typeof lastLcp.startTime === 'number')
2038
- reportMetric(report, options.reportType, 'LCP', lastLcp.startTime, { unit: 'ms' });
2039
- lastLcp = null;
2040
- }
2041
- catch {
2042
- // ignore
2043
- }
2044
- };
2045
- window.addEventListener('pagehide', flushLcp, { once: true });
2046
- document.addEventListener('visibilitychange', () => {
2047
- if (document.visibilityState === 'hidden')
2048
- flushLcp();
2049
- }, { once: true });
2050
2029
  }
2051
2030
  catch {
2052
2031
  // ignore
2053
2032
  }
2054
- // CLS
2033
+ // INP(替代 FID,2024年3月起成为核心指标)
2055
2034
  try {
2056
- let clsValue = 0;
2057
- const po = new PerformanceObserver((list) => {
2058
- try {
2059
- for (const entry of list.getEntries()) {
2060
- if (entry && entry.hadRecentInput)
2061
- continue;
2062
- if (typeof entry.value === 'number')
2063
- clsValue += entry.value;
2064
- }
2065
- }
2066
- catch {
2067
- // ignore
2068
- }
2035
+ onINP((metric) => {
2036
+ report(options.reportType, {
2037
+ pagePath: getPagePath(),
2038
+ metric: 'INP',
2039
+ value: metric.value,
2040
+ unit: 'ms',
2041
+ rating: metric.rating,
2042
+ id: metric.id,
2043
+ navigationType: metric.navigationType,
2044
+ });
2069
2045
  });
2070
- po.observe({ type: 'layout-shift', buffered: true });
2071
- const flushCls = () => {
2072
- try {
2073
- if (!sampleHit$1(options.sampleRate))
2074
- return;
2075
- reportMetric(report, options.reportType, 'CLS', clsValue, { unit: 'score' });
2076
- }
2077
- catch {
2078
- // ignore
2079
- }
2080
- };
2081
- window.addEventListener('pagehide', flushCls, { once: true });
2082
- document.addEventListener('visibilitychange', () => {
2083
- if (document.visibilityState === 'hidden')
2084
- flushCls();
2085
- }, { once: true });
2086
2046
  }
2087
2047
  catch {
2088
2048
  // ignore
2089
2049
  }
2090
- // FID
2050
+ // TTFB
2091
2051
  try {
2092
- const po = new PerformanceObserver((list) => {
2093
- try {
2094
- if (!sampleHit$1(options.sampleRate))
2095
- return;
2096
- for (const entry of list.getEntries()) {
2097
- const startTime = typeof entry.startTime === 'number' ? entry.startTime : -1;
2098
- const processingStart = typeof entry.processingStart === 'number' ? entry.processingStart : -1;
2099
- if (startTime >= 0 && processingStart >= 0) {
2100
- reportMetric(report, options.reportType, 'FID', processingStart - startTime, { unit: 'ms' });
2101
- break;
2102
- }
2103
- }
2104
- }
2105
- catch {
2106
- // ignore
2107
- }
2052
+ onTTFB((metric) => {
2053
+ report(options.reportType, {
2054
+ pagePath: getPagePath(),
2055
+ metric: 'TTFB',
2056
+ value: metric.value,
2057
+ unit: 'ms',
2058
+ rating: metric.rating,
2059
+ id: metric.id,
2060
+ navigationType: metric.navigationType,
2061
+ });
2108
2062
  });
2109
- po.observe({ type: 'first-input', buffered: true });
2110
2063
  }
2111
2064
  catch {
2112
2065
  // ignore
2113
2066
  }
2114
2067
  }
2115
- // Resource timing:资源加载耗时
2068
+ // Resource timing:资源加载耗时(web-vitals 不包含此功能,保留原有实现)
2116
2069
  if (options.resourceTiming && typeof globalThis.PerformanceObserver === 'function') {
2117
2070
  try {
2118
2071
  const po = new PerformanceObserver((list) => {
@@ -2124,9 +2077,25 @@ function installBrowserPerformanceMonitor(report, options) {
2124
2077
  if (!name || shouldIgnoreUrl(name, ignoreUrls))
2125
2078
  continue;
2126
2079
  const initiatorType = String(entry?.initiatorType ?? '');
2127
- // 对齐文档:关注 fetch/xhr/img/script(同时允许 css/other 但不强制)
2080
+ // 关注 fetch/xhr/img/script(同时允许 css/other 但不强制)
2128
2081
  if (!['xmlhttprequest', 'fetch', 'img', 'script', 'css'].includes(initiatorType))
2129
2082
  continue;
2083
+ // 时序分解(便于定位慢在哪个阶段)
2084
+ const domainLookupStart = entry.domainLookupStart ?? 0;
2085
+ const domainLookupEnd = entry.domainLookupEnd ?? 0;
2086
+ const connectStart = entry.connectStart ?? 0;
2087
+ const connectEnd = entry.connectEnd ?? 0;
2088
+ const requestStart = entry.requestStart ?? 0;
2089
+ const responseStart = entry.responseStart ?? 0;
2090
+ const responseEnd = entry.responseEnd ?? 0;
2091
+ const dns = domainLookupEnd - domainLookupStart;
2092
+ const tcp = connectEnd - connectStart;
2093
+ const ttfb = responseStart - requestStart;
2094
+ const download = responseEnd - responseStart;
2095
+ // 缓存检测:transferSize=0 且 decodedBodySize>0 表示缓存命中
2096
+ const transferSize = entry.transferSize ?? 0;
2097
+ const decodedBodySize = entry.decodedBodySize ?? 0;
2098
+ const cached = transferSize === 0 && decodedBodySize > 0;
2130
2099
  const payload = {
2131
2100
  pagePath: getPagePath(),
2132
2101
  metric: 'resource',
@@ -2134,16 +2103,26 @@ function installBrowserPerformanceMonitor(report, options) {
2134
2103
  url: truncate$1(name, options.maxTextLength),
2135
2104
  startTime: typeof entry?.startTime === 'number' ? entry.startTime : undefined,
2136
2105
  duration: typeof entry?.duration === 'number' ? entry.duration : undefined,
2106
+ // 时序分解(跨域资源可能为 0)
2107
+ dns: dns > 0 ? Math.round(dns) : undefined,
2108
+ tcp: tcp > 0 ? Math.round(tcp) : undefined,
2109
+ ttfb: ttfb > 0 ? Math.round(ttfb) : undefined,
2110
+ download: download > 0 ? Math.round(download) : undefined,
2111
+ // 缓存标记
2112
+ cached,
2137
2113
  };
2138
- // 兼容字段(部分浏览器支持)
2139
- if (typeof entry?.transferSize === 'number')
2140
- payload.transferSize = entry.transferSize;
2141
- if (typeof entry?.encodedBodySize === 'number')
2114
+ // 尺寸字段(跨域资源可能为 0)
2115
+ if (transferSize > 0)
2116
+ payload.transferSize = transferSize;
2117
+ if (typeof entry?.encodedBodySize === 'number' && entry.encodedBodySize > 0) {
2142
2118
  payload.encodedBodySize = entry.encodedBodySize;
2143
- if (typeof entry?.decodedBodySize === 'number')
2144
- payload.decodedBodySize = entry.decodedBodySize;
2145
- if (typeof entry?.nextHopProtocol === 'string')
2119
+ }
2120
+ if (decodedBodySize > 0)
2121
+ payload.decodedBodySize = decodedBodySize;
2122
+ // 协议和状态
2123
+ if (typeof entry?.nextHopProtocol === 'string' && entry.nextHopProtocol) {
2146
2124
  payload.nextHopProtocol = entry.nextHopProtocol;
2125
+ }
2147
2126
  if (typeof entry?.responseStatus === 'number')
2148
2127
  payload.status = entry.responseStatus;
2149
2128
  report(options.reportType, payload);
@@ -2171,7 +2150,6 @@ function installWebPerformanceMonitor(report, opts = {}) {
2171
2150
  sampleRate: raw.sampleRate ?? 1,
2172
2151
  ignoreUrls: raw.ignoreUrls ?? [],
2173
2152
  webVitals: raw.webVitals ?? true,
2174
- navigationTiming: raw.navigationTiming ?? true,
2175
2153
  resourceTiming: raw.resourceTiming ?? true,
2176
2154
  maxTextLength: raw.maxTextLength ?? 2000,
2177
2155
  };
@@ -2263,7 +2241,6 @@ function installMiniPerformanceMonitor(report, opts = {}) {
2263
2241
  sampleRate: raw.sampleRate ?? 1,
2264
2242
  ignoreUrls: raw.ignoreUrls ?? [],
2265
2243
  webVitals: raw.webVitals ?? true,
2266
- navigationTiming: raw.navigationTiming ?? true,
2267
2244
  resourceTiming: raw.resourceTiming ?? true,
2268
2245
  maxTextLength: raw.maxTextLength ?? 2000,
2269
2246
  };
package/dist/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var webVitals = require('web-vitals');
6
+
5
7
  function isPlainObject(value) {
6
8
  return Object.prototype.toString.call(value) === '[object Object]';
7
9
  }
@@ -1962,14 +1964,10 @@ function getPagePath() {
1962
1964
  return '';
1963
1965
  }
1964
1966
  }
1965
- function reportMetric(report, reportType, metric, value, extra = {}) {
1966
- report(reportType, {
1967
- pagePath: getPagePath(),
1968
- metric,
1969
- value,
1970
- ...extra,
1971
- });
1972
- }
1967
+ /**
1968
+ * 安装浏览器性能监控
1969
+ * 使用 Google web-vitals 库实现 Core Web Vitals 指标采集
1970
+ */
1973
1971
  function installBrowserPerformanceMonitor(report, options) {
1974
1972
  if (typeof window === 'undefined')
1975
1973
  return;
@@ -1978,145 +1976,100 @@ function installBrowserPerformanceMonitor(report, options) {
1978
1976
  return;
1979
1977
  w.__beLinkClsLoggerPerfInstalled__ = true;
1980
1978
  const ignoreUrls = [...DEFAULT_IGNORE, ...(options.ignoreUrls ?? [])];
1981
- // Navigation timing: TTFB
1982
- if (options.navigationTiming) {
1979
+ // Web Vitals: 使用 Google web-vitals 库
1980
+ // 这个库会自动处理:
1981
+ // - LCP 在用户交互后停止观察
1982
+ // - firstHiddenTime 过滤(页面隐藏后的数据不计入)
1983
+ // - CLS 5秒会话窗口算法
1984
+ // - BFCache 恢复时自动重置指标
1985
+ if (options.webVitals) {
1986
+ // FCP
1983
1987
  try {
1984
- const navEntries = performance.getEntriesByType?.('navigation');
1985
- const nav = Array.isArray(navEntries) ? navEntries[0] : undefined;
1986
- if (nav && typeof nav === 'object') {
1987
- const ttfb = typeof nav.responseStart === 'number' && typeof nav.requestStart === 'number'
1988
- ? nav.responseStart - nav.requestStart
1989
- : -1;
1990
- if (ttfb >= 0 && sampleHit$1(options.sampleRate))
1991
- reportMetric(report, options.reportType, 'TTFB', ttfb, { unit: 'ms' });
1992
- }
1988
+ webVitals.onFCP((metric) => {
1989
+ report(options.reportType, {
1990
+ pagePath: getPagePath(),
1991
+ metric: 'FCP',
1992
+ value: metric.value,
1993
+ unit: 'ms',
1994
+ rating: metric.rating,
1995
+ id: metric.id,
1996
+ navigationType: metric.navigationType,
1997
+ });
1998
+ });
1993
1999
  }
1994
2000
  catch {
1995
2001
  // ignore
1996
2002
  }
1997
- }
1998
- // Web Vitals: FCP/LCP/CLS/FID
1999
- if (options.webVitals && typeof globalThis.PerformanceObserver === 'function') {
2000
- // FCP
2003
+ // LCP(自动处理用户交互后停止)
2001
2004
  try {
2002
- const po = new PerformanceObserver((list) => {
2003
- try {
2004
- if (!sampleHit$1(options.sampleRate))
2005
- return;
2006
- for (const entry of list.getEntries()) {
2007
- if (entry?.name === 'first-contentful-paint' && typeof entry.startTime === 'number') {
2008
- reportMetric(report, options.reportType, 'FCP', entry.startTime, { unit: 'ms' });
2009
- }
2010
- }
2011
- }
2012
- catch {
2013
- // ignore
2014
- }
2005
+ webVitals.onLCP((metric) => {
2006
+ report(options.reportType, {
2007
+ pagePath: getPagePath(),
2008
+ metric: 'LCP',
2009
+ value: metric.value,
2010
+ unit: 'ms',
2011
+ rating: metric.rating,
2012
+ id: metric.id,
2013
+ navigationType: metric.navigationType,
2014
+ });
2015
2015
  });
2016
- po.observe({ type: 'paint', buffered: true });
2017
2016
  }
2018
2017
  catch {
2019
2018
  // ignore
2020
2019
  }
2021
- // LCP(最后一次为准)
2022
- let lastLcp = null;
2020
+ // CLS(自动处理会话窗口)
2023
2021
  try {
2024
- const po = new PerformanceObserver((list) => {
2025
- try {
2026
- const entries = list.getEntries();
2027
- if (entries && entries.length)
2028
- lastLcp = entries[entries.length - 1];
2029
- }
2030
- catch {
2031
- // ignore
2032
- }
2022
+ webVitals.onCLS((metric) => {
2023
+ report(options.reportType, {
2024
+ pagePath: getPagePath(),
2025
+ metric: 'CLS',
2026
+ value: metric.value,
2027
+ unit: 'score',
2028
+ rating: metric.rating,
2029
+ id: metric.id,
2030
+ navigationType: metric.navigationType,
2031
+ });
2033
2032
  });
2034
- po.observe({ type: 'largest-contentful-paint', buffered: true });
2035
- const flushLcp = () => {
2036
- try {
2037
- if (!lastLcp)
2038
- return;
2039
- if (!sampleHit$1(options.sampleRate))
2040
- return;
2041
- if (typeof lastLcp.startTime === 'number')
2042
- reportMetric(report, options.reportType, 'LCP', lastLcp.startTime, { unit: 'ms' });
2043
- lastLcp = null;
2044
- }
2045
- catch {
2046
- // ignore
2047
- }
2048
- };
2049
- window.addEventListener('pagehide', flushLcp, { once: true });
2050
- document.addEventListener('visibilitychange', () => {
2051
- if (document.visibilityState === 'hidden')
2052
- flushLcp();
2053
- }, { once: true });
2054
2033
  }
2055
2034
  catch {
2056
2035
  // ignore
2057
2036
  }
2058
- // CLS
2037
+ // INP(替代 FID,2024年3月起成为核心指标)
2059
2038
  try {
2060
- let clsValue = 0;
2061
- const po = new PerformanceObserver((list) => {
2062
- try {
2063
- for (const entry of list.getEntries()) {
2064
- if (entry && entry.hadRecentInput)
2065
- continue;
2066
- if (typeof entry.value === 'number')
2067
- clsValue += entry.value;
2068
- }
2069
- }
2070
- catch {
2071
- // ignore
2072
- }
2039
+ webVitals.onINP((metric) => {
2040
+ report(options.reportType, {
2041
+ pagePath: getPagePath(),
2042
+ metric: 'INP',
2043
+ value: metric.value,
2044
+ unit: 'ms',
2045
+ rating: metric.rating,
2046
+ id: metric.id,
2047
+ navigationType: metric.navigationType,
2048
+ });
2073
2049
  });
2074
- po.observe({ type: 'layout-shift', buffered: true });
2075
- const flushCls = () => {
2076
- try {
2077
- if (!sampleHit$1(options.sampleRate))
2078
- return;
2079
- reportMetric(report, options.reportType, 'CLS', clsValue, { unit: 'score' });
2080
- }
2081
- catch {
2082
- // ignore
2083
- }
2084
- };
2085
- window.addEventListener('pagehide', flushCls, { once: true });
2086
- document.addEventListener('visibilitychange', () => {
2087
- if (document.visibilityState === 'hidden')
2088
- flushCls();
2089
- }, { once: true });
2090
2050
  }
2091
2051
  catch {
2092
2052
  // ignore
2093
2053
  }
2094
- // FID
2054
+ // TTFB
2095
2055
  try {
2096
- const po = new PerformanceObserver((list) => {
2097
- try {
2098
- if (!sampleHit$1(options.sampleRate))
2099
- return;
2100
- for (const entry of list.getEntries()) {
2101
- const startTime = typeof entry.startTime === 'number' ? entry.startTime : -1;
2102
- const processingStart = typeof entry.processingStart === 'number' ? entry.processingStart : -1;
2103
- if (startTime >= 0 && processingStart >= 0) {
2104
- reportMetric(report, options.reportType, 'FID', processingStart - startTime, { unit: 'ms' });
2105
- break;
2106
- }
2107
- }
2108
- }
2109
- catch {
2110
- // ignore
2111
- }
2056
+ webVitals.onTTFB((metric) => {
2057
+ report(options.reportType, {
2058
+ pagePath: getPagePath(),
2059
+ metric: 'TTFB',
2060
+ value: metric.value,
2061
+ unit: 'ms',
2062
+ rating: metric.rating,
2063
+ id: metric.id,
2064
+ navigationType: metric.navigationType,
2065
+ });
2112
2066
  });
2113
- po.observe({ type: 'first-input', buffered: true });
2114
2067
  }
2115
2068
  catch {
2116
2069
  // ignore
2117
2070
  }
2118
2071
  }
2119
- // Resource timing:资源加载耗时
2072
+ // Resource timing:资源加载耗时(web-vitals 不包含此功能,保留原有实现)
2120
2073
  if (options.resourceTiming && typeof globalThis.PerformanceObserver === 'function') {
2121
2074
  try {
2122
2075
  const po = new PerformanceObserver((list) => {
@@ -2128,9 +2081,25 @@ function installBrowserPerformanceMonitor(report, options) {
2128
2081
  if (!name || shouldIgnoreUrl(name, ignoreUrls))
2129
2082
  continue;
2130
2083
  const initiatorType = String(entry?.initiatorType ?? '');
2131
- // 对齐文档:关注 fetch/xhr/img/script(同时允许 css/other 但不强制)
2084
+ // 关注 fetch/xhr/img/script(同时允许 css/other 但不强制)
2132
2085
  if (!['xmlhttprequest', 'fetch', 'img', 'script', 'css'].includes(initiatorType))
2133
2086
  continue;
2087
+ // 时序分解(便于定位慢在哪个阶段)
2088
+ const domainLookupStart = entry.domainLookupStart ?? 0;
2089
+ const domainLookupEnd = entry.domainLookupEnd ?? 0;
2090
+ const connectStart = entry.connectStart ?? 0;
2091
+ const connectEnd = entry.connectEnd ?? 0;
2092
+ const requestStart = entry.requestStart ?? 0;
2093
+ const responseStart = entry.responseStart ?? 0;
2094
+ const responseEnd = entry.responseEnd ?? 0;
2095
+ const dns = domainLookupEnd - domainLookupStart;
2096
+ const tcp = connectEnd - connectStart;
2097
+ const ttfb = responseStart - requestStart;
2098
+ const download = responseEnd - responseStart;
2099
+ // 缓存检测:transferSize=0 且 decodedBodySize>0 表示缓存命中
2100
+ const transferSize = entry.transferSize ?? 0;
2101
+ const decodedBodySize = entry.decodedBodySize ?? 0;
2102
+ const cached = transferSize === 0 && decodedBodySize > 0;
2134
2103
  const payload = {
2135
2104
  pagePath: getPagePath(),
2136
2105
  metric: 'resource',
@@ -2138,16 +2107,26 @@ function installBrowserPerformanceMonitor(report, options) {
2138
2107
  url: truncate$1(name, options.maxTextLength),
2139
2108
  startTime: typeof entry?.startTime === 'number' ? entry.startTime : undefined,
2140
2109
  duration: typeof entry?.duration === 'number' ? entry.duration : undefined,
2110
+ // 时序分解(跨域资源可能为 0)
2111
+ dns: dns > 0 ? Math.round(dns) : undefined,
2112
+ tcp: tcp > 0 ? Math.round(tcp) : undefined,
2113
+ ttfb: ttfb > 0 ? Math.round(ttfb) : undefined,
2114
+ download: download > 0 ? Math.round(download) : undefined,
2115
+ // 缓存标记
2116
+ cached,
2141
2117
  };
2142
- // 兼容字段(部分浏览器支持)
2143
- if (typeof entry?.transferSize === 'number')
2144
- payload.transferSize = entry.transferSize;
2145
- if (typeof entry?.encodedBodySize === 'number')
2118
+ // 尺寸字段(跨域资源可能为 0)
2119
+ if (transferSize > 0)
2120
+ payload.transferSize = transferSize;
2121
+ if (typeof entry?.encodedBodySize === 'number' && entry.encodedBodySize > 0) {
2146
2122
  payload.encodedBodySize = entry.encodedBodySize;
2147
- if (typeof entry?.decodedBodySize === 'number')
2148
- payload.decodedBodySize = entry.decodedBodySize;
2149
- if (typeof entry?.nextHopProtocol === 'string')
2123
+ }
2124
+ if (decodedBodySize > 0)
2125
+ payload.decodedBodySize = decodedBodySize;
2126
+ // 协议和状态
2127
+ if (typeof entry?.nextHopProtocol === 'string' && entry.nextHopProtocol) {
2150
2128
  payload.nextHopProtocol = entry.nextHopProtocol;
2129
+ }
2151
2130
  if (typeof entry?.responseStatus === 'number')
2152
2131
  payload.status = entry.responseStatus;
2153
2132
  report(options.reportType, payload);
@@ -2175,7 +2154,6 @@ function installWebPerformanceMonitor(report, opts = {}) {
2175
2154
  sampleRate: raw.sampleRate ?? 1,
2176
2155
  ignoreUrls: raw.ignoreUrls ?? [],
2177
2156
  webVitals: raw.webVitals ?? true,
2178
- navigationTiming: raw.navigationTiming ?? true,
2179
2157
  resourceTiming: raw.resourceTiming ?? true,
2180
2158
  maxTextLength: raw.maxTextLength ?? 2000,
2181
2159
  };
@@ -2267,7 +2245,6 @@ function installMiniPerformanceMonitor(report, opts = {}) {
2267
2245
  sampleRate: raw.sampleRate ?? 1,
2268
2246
  ignoreUrls: raw.ignoreUrls ?? [],
2269
2247
  webVitals: raw.webVitals ?? true,
2270
- navigationTiming: raw.navigationTiming ?? true,
2271
2248
  resourceTiming: raw.resourceTiming ?? true,
2272
2249
  maxTextLength: raw.maxTextLength ?? 2000,
2273
2250
  };