浏览器原理
Navigation Timing v1 一共 21 个字段
const [entry] = performance.getEntriesByType("navigation");
console.table(entry.toJSON());
1.0 API
navigationStart:
此属性必须在用户代理完成提示卸载前一个文档后立即返回时间。如果没有以前的文档,该属性必须返回当前文档的创建时间。
没有为 定义此属性 PerformanceNavigationTiming。相反,作者可以使用 timeOrigin 来获取等效的时间戳。
unloadEventStart
如果前一个文档和当前文档具有相同的 来源,则该属性必须返回用户代理开始前一个文档的 卸载事件之前的时间。如果没有先前的文档或先前的文档与当前文档 的来源不同,则此属性必须返回零。
unloadEventEnd
如果上一个文档和当前文档具有 相同的
origin
,则该属性必须返回用户代理完成上一个文档的卸载事件后的时间。如果没有先前的文档或先前的文档与当前文档的来源不同或卸载尚未完成, 则此属性必须返回零。 如果导航时存在HTTP
重定向,并且并非所有重定向都来自同一来源,则两者都PerformanceTiming
,unloadEventStart
和PerformanceTiming
。unloadEventEnd
必须返回零。
redirectStart
如果导航时存在
HTTP
重定向,并且所有重定向都来自同一个origin
,则此属性必须返回 启动重定向的fetch
的开始时间。否则,此属性必须返回零。
redirectEnd
如果导航时有
HTTP
重定向,并且所有重定向都来自同一个origin
,则该属性必须在收到最后一个重定向响应的最后一个字节后立即返回时间。否则,此属性必须返回零。
fetchStart
如果要 使用“GET”请求方法获取新资源,则
fetchStart
必须立即返回用户代理开始检查HTTP
缓存之前的时间。否则,它必须返回用户代理开始获取资源的时间。
domainLookupStart
该属性必须返回用户代理开始查找当前文档的域名之前的时间。如果使用持久连接 或从
HTTP
缓存或本地资源中检索当前文档,则此属性必须返回与相同的值PerformanceTiming
。fetchStart
。
domainLookupEnd
此属性必须在用户代理完成当前文档的域名查找后立即返回时间。如果使用持久连接 或从
HTTP
缓存或本地资源中检索当前文档,则此属性必须返回与相同的值PerformanceTiming
。fetchStart
。
从 HTTP
缓存 检查和检索内容是获取过程的一部分。它被 PerformanceTiming
, requestStart
, PerformanceTiming
, responseStart
和 PerformanceTiming
。 responseEnd
属性。
如果用户代理已经在缓存中拥有域信息,则 domainLookupStart
和 domainLookupEnd
表示用户代理开始和结束从缓存中检索域数据的时间。
connectStart
该属性必须返回用户代理开始与服务器建立连接以检索文档之前的时间。如果使用持久连接 或从
HTTP
缓存或本地资源中检索当前文档,则此属性必须返回 的值PerformanceTiming
。domainLookupEnd
。
connectEnd
此属性必须在用户代理完成与服务器的连接以检索当前文档后立即返回时间。如果使用持久连接 或从
HTTP
缓存或本地资源中检索当前文档,则此属性必须返回 的值PerformanceTiming
。domainLookupEnd
。
如果传输连接失败并且用户代理重新打开连接,
PerformanceTiming
,connectStart
和PerformanceTiming
。connectEnd
应该返回新连接的相应值。PerformanceTiming
。connectEnd
必须包括建立传输连接的时间间隔以及其他时间间隔,例如SSL
握手和SOCKS
身份验证。
secureConnectionStart
该属性是可选的。没有此属性可用的用户代理必须将其设置为未定义。当此属性可用时,如果当前页面的方案
[ URL ]
为https
,则该属性必须返回用户代理开始握手过程之前的时间以保护当前连接。 如果此属性可用但未使用HTTPS
,则此属性必须返回零。
requestStart
此属性必须立即返回用户代理开始从服务器、
HTTP
缓存或本地资源请求当前文档之前的时间。 如果在发送请求后传输连接失败并且用户代理重新打开连接并重新发送请求,PerformanceTiming
,requestStart
应该返回新请求的相应值。
该接口不包含表示请求发送完成的属性,例如
requestEnd
。- 用户代理发送请求的完成并不总是表示网络传输中相应的完成时间,这带来了拥有这样一个属性的大部分好处。
- 由于
HTTP
层封装,一些用户代理在确定发送请求的实际完成时间时成本很高。
responseStart
此属性必须在用户代理从服务器、
HTTP
缓存或本地资源接收到响应的第一个字节后立即返回时间。
responseEnd
该属性必须返回用户代理收到当前文档的最后一个字节之后或传输连接关闭之前的时间,以先到者为准。此处的文档可以从服务器、
HTTP
缓存或本地资源接收。
domLoading
此属性必须返回用户代理将
current document readiness
设置为loading
之前的时间。
由于现有用户代理中创建 Document
对象的时间不同,返回的值 domLoading
是特定于实现的,不应在有意义的指标中使用。
domInteractive
该属性必须返回用户代理将
current document readiness
设置为“ interactive 交互式”之前的时间。
domContentLoadedEventStart
该属性必须返回用户代理在
Document
上触发DOMContentLoaded
事件之前的时间。
domContentLoadedEventEnd
此属性必须在文档的
DOMContentLoaded
事件完成后立即返回时间。
domComplete
该属性必须返回用户代理
current document readiness
在 "complete" 之前的时间。
如果当前文档就绪状态多次更改为相同状态, PerformanceTiming
. domLoading
, PerformanceTiming
. domInteractive
, PerformanceTiming
,
domContentLoadedEventStart
, PerformanceTiming
. domContentLoadedEventEnd
和 PerformanceTiming
。必须返回相应文档就绪 更改 domComplete
第一次发生的时间。
loadEventStart
该属性必须返回触发当前文档的加载事件之前的时间。当尚未触发加载事件时,它必须返回零。
loadEventEnd
该属性必须返回当前文档的加载事件完成的时间。当加载事件未触发或未完成时,它必须返回零。
window.performance.timing; // Timing标准
window.performance.getEntriesByType("navigation")[0]; // Timing2标准
- 白屏时间
- 从 navigatorStart 到 responseEnd 这段时间算作服务器时间。
- 从浏览器开始加载页面到首次出现内容之前的这段时间(从页面发送一个页面 URL 请求出去,到服务器返回这个 HTML 的文本内容)。
performance.timing.responseEnd - performance.timing.navigationStart;
performance.getEntriesByType("navigation")[0].responseStart;
- 浏览器渲染时间
- 从 responseEnd 到 loadEventEnd 这段时间,包括 CSS、JavaScript、Image 等资源的加载耗时。
performance.timing.loadEventEnd - performance.timing.responseEnd;
performance.getEntriesByType("navigation")[0].loadEventEnd -
performance.getEntriesByType("navigation")[0].responseStart;
- 整页时间
- 从浏览器开始加载页面到整个页面加载完毕(最明显的标识就是移动端浏览器进度条读完了,pc 端浏览器就是当前页签前面的 loading 消失了)
performance.timing.loadEventEnd - performance.timing.navigationStart;
performance.getEntriesByType("navigation")[0].loadEventEnd;
基础版本
- 浏览器根据请求的
URL
交给DNS
域名解析,找到真实IP
,向服务器发起请求; - 服务器交给后台处理完成后返回数据,浏览器接收⽂件(
HTML
、JS
、CSS
、图象等); - 浏览器对加载到的资源(
HTML
、JS
、CSS
等)进⾏语法解析,建⽴相应的内部数据结构(如HTML
的DOM
); - 载⼊解析到的资源⽂件,渲染⻚⾯,完成。
详细版
在浏览器地址栏输⼊ URL
浏览器查看缓存,如果请求资源在缓存中并且新鲜,重定向到转码步骤
如果资源未缓存,发起新请求
如果已缓存,检验是否⾜够新鲜,⾜够新鲜直接提供给客户端,否则与服务器进⾏验证。
检验 新鲜 通常有两个 HTTP 头进⾏控制 Expires 和 Cache-Control : HTTP1.0 提供 Expires,值为⼀个绝对时间表示缓存新鲜⽇期 HTTP1.1 增加了 Cache-Control: max-age=,值为以秒为单位的最⼤新鲜时间
浏览器解析 URL 获取协议,主机,端⼝,path
浏览器组装⼀个 HTTP(GET)请求报⽂
浏览器获取主机 ip 地址,过程如下:
浏览器缓存
本机缓存
hosts ⽂件
路由器缓存
ISP DNS 缓存
DNS 递归查询(可能存在负载均衡导致每次 IP 不⼀样)
打开⼀个 socket 与⽬标 IP 地址,端⼝建⽴ TCP 链接,三次握⼿如下:
客户端发送⼀个 TCP 的 SYN=1,Seq=X 的包到服务器端⼝
服务器发回 SYN=1, ACK=X+1, Seq=Y 的响应包
客户端发送 ACK=Y+1, Seq=Z
TCP 链接建⽴后发送 HTTP 请求
服务器接受请求并解析,将请求转发到服务程序,如虚拟主机使⽤ HTTP Host 头部判断请求的服务程序
服务器检查 HTTP 请求头是否包含缓存验证信息如果验证缓存新鲜,返回 304 等对应状态码
处理程序读取完整请求并准备 HTTP 响应,可能需要查询数据库等操作
服务器将响应报⽂通过 TCP 连接发送回浏览器
浏览器接收 HTTP 响应,然后根据情况选择关闭 TCP 连接或者保留重⽤,关闭 TCP 连接的四次握⼿如下:
主动⽅发送 Fin=1, Ack=Z, Seq= X 报⽂
被动⽅发送 ACK=X+1, Seq=Z 报⽂
被动⽅发送 Fin=1, ACK=X, Seq=Y 报⽂
主动⽅发送 ACK=Y, Seq=X 报⽂
浏览器检查响应状态吗:是否为 1XX,3XX, 4XX, 5XX,这些情况处理与 2XX 不同
如果资源可缓存,进⾏缓存
对响应进⾏解码(例如 gzip 压缩)
根据资源类型决定如何处理(假设资源为 HTML ⽂档)
解析 HTML ⽂档,构件 DOM 树,下载资源,构造 CSSOM 树,执⾏ js 脚本,这些操作没有严格的先后顺序,以下分别解释
构建 DOM 树:
Tokenizing:根据 HTML 规范将字符流解析为标记
Lexing:词法分析将标记转换为对象并定义属性和规则
DOM construction:根据 HTML 标记关系将对象组成 DOM 树
解析过程中遇到图⽚、样式表、js ⽂件,启动下载
构建 CSSOM 树:
Tokenizing:字符流转换为标记流
Node:根据标记创建节点
CSSOM:节点创建 CSSOM 树
根据 DOM 树和 CSSOM 树构建渲染树 :
从 DOM 树的根节点遍历所有可⻅节点,不可⻅节点包括:1) script , meta 这样本身 不可⻅的标签。2)被 css 隐藏的节点,如 display: none
对每⼀个可⻅节点,找到恰当的 CSSOM 规则并应⽤
发布可视节点的内容和计算样式
js 解析如下:
浏览器创建 Document 对象并解析 HTML,将解析到的元素和⽂本节点添加到⽂档中,此时 document.readystate 为 loading
HTML 解析器遇到没有 async 和 defer 的 script 时,将他们添加到⽂档中,然后执⾏⾏内或外部脚本。这些脚本会同步执⾏,并且在脚本下载和执⾏时解析器会暂停。这样就可 以⽤ document.write()把⽂本插⼊到输⼊流中。同步脚本经常简单定义函数和注册事件处理程序,他们可以遍历和操作 script 和他们之前的⽂档内容
当解析器遇到设置了 async 属性的 script 时,开始下载脚本并继续解析⽂档。脚本会在它下载完成后尽快执⾏,但是解析器不会停下来等它下载。异步脚本禁⽌使⽤ document.write(),它们可以访问⾃⼰ script 和之前的⽂档元素
当⽂档完成解析,document.readState 变成 interactive
所有 defer 脚本会按照在⽂档出现的顺序执⾏,延迟脚本能访问完整⽂档树,禁⽌使⽤ document.write()
浏览器在 Document 对象上触发 DOMContentLoaded 事件
此时⽂档完全解析完成,浏览器可能还在等待如图⽚等内容加载,等这些内容完成载⼊并且所有异步脚本完成载⼊和执⾏,document.readState 变为 complete,window 触发 load 事件
显示⻚⾯(HTML 解析过程中会逐步显示⻚⾯)
根据性能指标总结的优化手段
页面加载的时间段
- navigation-time2
卸载旧页面 -> redirect 时间(这不叫重定向,虽然单词的意思是重定向但是在这里是缓存策略时间段) -> ServiceWork 初始化 如果有就 ServiceWork 进行 Fetch Event -> HTTP 缓存获取阶段 -> DNS 查询时间段 -> TCP 连接时间 -> Request 请求时间 -> Response 请求响应时间 -> DOM 解析时间 -> DOM 渲染时间 -> Load 事件执行时间
- navigation-time2