说说PerformanceNavigationTiming,怎么来进行性能优化

135 阅读4分钟

按获取文档时记录的时间戳顺序排列的时间戳图

mdn/PerformanceNavigationTiming 中可以看到所有的时间戳属性,但是做性能优化核心并不单单获知时间戳名,而是时间戳代表什么,怎么利用时间戳判断前端性能好坏和问题来源,进一步地说,就是找到需要关注的时间戳和时间段

数据获取

const observer = new PerformanceObserver(list => { 
    list.getEntries().forEach(entry => {    
        console.log(entry);  
    });
});
observer.observe({ type: 'navigation', buffered: true });

同事件类型时间段

如果时间戳名类似于 XXXEndXXXStart,那么这是事件型的时间戳,分别代表事件的开始和结束,二者之间的差值就是其耗时,比如:

  1. 重定向时间 redirectEnd - redirectStart
  2. TCP 连接 connectEnd - connectStart
  3. DNS 查询 domainLookupEnd - domainLookupStart
  4. 内容传输 responseEnd - responseStart

并不是所有的时间戳都一定有对应开始和结束,比如存在 workStart 代表 serviceWorker 启动,但是不存在 workEnd 代表结束,比如存在 fetchStart 代表请求开始,但是没有 fetchEnd 来表示请求结束

三大核心指标

前人总结经验,有三个核心指标和性能优化关系较大

  1. 首字节 responseStart - fetchStart
  2. DOM Ready domContentLoadedEventEnd - fetchStart
  3. 页面完全加载 loadEventStart - fetchStart

不难观察出一点,三个指标都和 fetchStart 相关,因为 fetchStart 代表请求开始

首字节

  1. 首字节时间 = TTFB = responseStart - fetchStart
  2. mdn/TTFB 中写明了 TTFB 包括 DNS、TCP、TLS,所以 TTFB 的时间起点应该是 fetchStart,而不是 requestStart
  3. 部分资料会提到 TTFB 的时间起点是 requestStart,这个可能是 中文mdn 误导
  4. 怀疑翻译导致的,fetchStart 被翻译成请求开始,然后又被理解为 requestStart

mdn 关于 TTFB 原文

Time to First Byte (TTFB) refers to the time between the browser requesting a page and when it receives the first byte of information from the server. This time includes DNS lookup and establishing the connection using a TCP handshake and TLS handshake if the request is made over HTTPS.

DOM 解析完成

  1. DOM Ready = HTML解析完成时间 = domContentLoadedEventEnd - fetchStart

  2. domContentLoadedEvent 简称 dcl,是早年前端性能优化的重点

  3. domInteractive、dcl、domComplete 的关系

  4. 执行顺序:dominteractive、defer script、dcl、加载资源、domComplete

  5. async script 不保证加载和执行顺序,自然不绑定时间戳

  6. dom 时间戳存在和 document.readyState 的对应关系,是曾经 dom 时间戳的判断方法

  7. dom 解析开始时间

  8. 一般认为约等于 responseEnd

  9. 存在流式解析时,可能早于 responseEnd

  10. 同样的,dom 解析时间可以理解为 domInteractive - responseEnd

  11. 同步脚本阻塞可能推迟这个时间,同时 HTML Preload Scanner 让部分资源请求早于 domInteractive

  12. 如果有 async、defer的话,其可能会在 domInteractive 之后才执行

页面完全加载

  1. 页页面完全加载 loadEventStart - fetchStart
  2. 对应的,资源加载耗时就是 loadEventStart - domContentLoadedEventEnd
  3. 值得说的是,其实 loadEventStart - domComplete 约等于 0,也就是说资源加载主要是 domComplete - domContentLoadedEventEnd

当前版本三大核心指标意义有所下降

用户感知 ≠ 技术事件

  1. 用户只关心“最快能看到可交互内容”,即 FP、FCP、LCP;

  2. 这些指标往往发生在 dom 尚未完成、甚至 js、css 还没下载完的阶段;

  3. 而 DOM Ready 发生时,可能首屏早就渲染好了,继续优化它意义有限

SPA / 渐进式渲染弱化了“整页”概念

  1. SPA / 渐进式渲染弱化了“整页”概念

  2. 单页应用首帧是一张空壳,后续路由切换全靠 js 动态拼装 dom;

  3. 首次包 + 关键资源 才是瓶颈,DOM Ready 只表示“外壳”好了,业务模块可能还没下载;

HTTP/2、HTTP/3、Service Worker 让后端首字节 & 网络栈并行化

  1. 过去 DNS+TCP+TLS 串行耗时大,TTFB 影响明显;

  2. 现在多路复用、连接复用、预连接、预加载把首字节提前

  3. TTFB 本身也不是“性能瓶颈”而是变成了“网络可用性”,被用作“网络链路 + 服务器处理”健康度报警

http 缓存相关

http 缓存存在且命中的情况下

  1. responseEnd - fetchStart 约等于 0

  2. transferSize = 0 强缓存,transferSize 很小是协商缓存

  3. 无缓存的时候可能 transferSize > encodedBodySize(相应头)

  4. 一般谈论 navigation 时指 html 文件,而 html 文件不做缓存,如果是类似 PerformanceResourceTiming 涉及其他资源,才会出现缓存

如果 serviceworker 命中缓存也有类似情况(不考虑回源、内部处理耗时)