logTimingOnce 是啥 + 前端性能计时的正规做法

有人问 logTimingOnce 是 JS 内置什么函数——不是。这个名字不是浏览器标准 API,也不在 Node 内置里。多半是:

  • 某个项目里自己定义的工具函数
  • 某个框架 / SDK 内部封装
  • 被压缩混淆后的名字

从名字看是 log + Timing + Once——只记录一次耗时日志。列几种可能的实现和前端性能计时的正规做法。

猜测的实现

版本 1:一次性计时器

function logTimingOnce(name, fn) {
  const start = performance.now();
  const result = fn();
  const end = performance.now();
  console.log(`${name}: ${(end - start).toFixed(2)}ms`);
  return result;
}

// 用法
const data = logTimingOnce("parseJSON", () => JSON.parse(bigStr));

版本 2:同一 key 只打一次

const logged = new Set();

function logTimingOnce(key, cost) {
  if (logged.has(key)) return;
  logged.add(key);
  console.log(`[timing] ${key}: ${cost}ms`);
}

// 用法
const start = performance.now();
initWebGL();
logTimingOnce("webgl-init", performance.now() - start);   // 只打一次

大型 SPA / 游戏 / WebGL / AI 前端里这种模式常见——首屏耗时、初始化时间只统计一次。

想知道具体项目里 logTimingOnce 是哪种,直接:

console.log(logTimingOnce.toString());

正规姿势:performance.now

Date.now() 精度只到毫秒,且系统时钟被调整还会跳(NTP 校时、用户改时间)。计时永远用 performance.now()

const start = performance.now();
// ... 做事
const cost = performance.now() - start;
console.log(cost);   // 精度到微秒(0.001ms)

单调时钟,不会倒退,浏览器和 Node 都支持。

User Timing API:让 DevTools 看得见

上面的 console.log 输出到控制台,看着乱。用 User Timing API 把标记打到 Performance 面板

performance.mark("webgl-init-start");
initWebGL();
performance.mark("webgl-init-end");

performance.measure(
  "webgl-init",
  "webgl-init-start",
  "webgl-init-end"
);

之后 Chrome DevTools → Performance 面板里能看到 webgl-init 这条竖线,跟其它内置事件(LCP、FCP)在一起。

拿测量结果:

const [entry] = performance.getEntriesByName("webgl-init");
console.log(entry.duration);

PerformanceObserver:订阅性能事件

要监听所有 measure(比如上报到日志系统):

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.duration);
    // 上报到你的日志服务
  }
});
observer.observe({ entryTypes: ["measure"] });

也能监听浏览器内置事件:

// LCP(Largest Contentful Paint)
new PerformanceObserver(list => {
  const last = list.getEntries().pop();
  console.log("LCP:", last.startTime);
}).observe({ type: "largest-contentful-paint", buffered: true });

常用性能指标一览

指标什么意思怎么拿
FCP (First Contentful Paint)首次有内容渲染paint entryType
LCP (Largest Contentful Paint)最大内容渲染largest-contentful-paint
FID (First Input Delay) / INP首次 / 最长交互延迟eventfirst-input
CLS (Cumulative Layout Shift)布局偏移layout-shift
TTFB (Time to First Byte)首字节时间navigation entryType

想一次性拿 Web Vitals,直接用 Google 的库:

npm install web-vitals
import { onCLS, onLCP, onINP } from "web-vitals";
onCLS(console.log);
onLCP(console.log);
onINP(console.log);

顺带:JS 生成 UUID

原对话里还问了 UUID,顺便说下——现代浏览器和 Node 直接用

crypto.randomUUID();
// "3f6c2a6f-8d7d-4f9c-b7d5-3fcb52c8a1aa"

比自己写的 Math.random 版本安全(用 CSPRNG)、代码短。老浏览器不支持时才需要 polyfill。

一句话总结

logTimingOnce 是项目自定义函数,不是标准 API。真正测性能performance.now() 做基础,performance.mark/measure 让 DevTools 看得见,PerformanceObserver 采集上报。UUID 直接 crypto.randomUUID()