Description
- Attributes underlined may not be available in navigation involving documents from different origins
通过这个过程理解浏览器工作流程。
Performance
当前页面的性能相关数据。
- Performance Timeline API
- High Resolution Time API
- Navigation Timing API
- User Timing API
- Resource Timing API
PerformanceTiming
1. navigationStart
当前浏览器tab里上个页面确定unload(beforeunload之后),开始处理当前页面的时间点(毫秒,时间戳)。
2. unloadEventStart
- 上个页面第一个
unload
事件处理函数执行的时间点。 sameOrigin
限制;- 没法记录
beforeunload
事件处理函数的执行时间。
3. unloadEventEnd
- 上个页面最后一个
unload
事件处理函数执行完的时间点; sameOrigin
限制
page1:
<!DOCTYPE html>
<html>
<head>
<title>Performance</title>
</head>
<body>
<div>
<h1>Performance Page1</h1>
<a href="./index2">Next></a>
</div>
<script>
window.onunload = () => {
var now = Date.now();
while(Date.now() - now < 3000) {
}
}
</script>
</body>
</html>
page2:
<!DOCTYPE html>
<html>
<head>
<title>Performance</title>
</head>
<body>
<div>
<h1>Performance Page2</h1>
</div>
<script>
;(function() {
var timing = window.performance.timing;
console.log(`unload Time: ${timing.unloadEventEnd - timing.unloadEventStart}`);
})()
</script>
</body>
</html>
page1跳转到page2:
unload Time: 3000
注意:
当前页面的unload
事件触发和请求新的页面是并行的(如上图),浏览器会并行请求新的页面。只不过要等unload
事件处理函数执行完后,才会解析当面的DOM。
4. redirectStart
首次重定向(3XX)开始时间点。
如:页面A导航到页面B,页面B重定向到页面C,页面C又重定向到页面D,对于页面D来说 redirectStart
表示就是开始“重定向到页面C”的时间点。
重定向过程中只要存在非同源url,则就取值为0。
5. redirectEnd
最后一个重定向完成时间点(Response最后一个字节接收到时间点)。
如:页面A导航到页面B,页面B重定向到页面C,页面C又重定向到页面D,对于页面D来说 redirectEnd
表示就是页面C的响应结束时间点(when the last byte of the HTTP response has been received)。
重定向过程中只要存在非同源url,则就取值为0。
6. fetchStart
开始准备请求当前页面文档了时间点。跟navigationStart
的区别两者之间夹杂着redireactStart
和redireactEnd
。
如果存在重定向(即redireactStart
和redireactEnd
非0),则fetchStart
等于redireactEnd
;
否则fetchStart
约等于navigationStart
,实际比navigationStart
小一点;
Demo
页面a
, 点击【redirect】依次打开-> 页面b
-> 页面c
-> 页面d
。
app.get('/a', (req, res) => {
res.end(`
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Performance</h1>
<a href="http://localhost:8088/b">Redirect</a>
<script type="text/javascript">
;(function() {
function sleep(delay) {
var now = Date.now();
delay = delay || 500;
while(Date.now() - now < delay) {}
}
window.onbeforeunload = function(e) {
console.log('onbeforeunload');
e.preventDefault();
e.returnValue = '';
sleep();
}
window.onunload = function(e) {
console.log('onunload');
e.preventDefault();
e.returnValue = 'hello';
sleep(5000);
}
})()
</script>
</body>
</html>
`);
})
app.get('/b', (req, res) => {
res.setHeader('location', 'http://localhost:8088/c');
res.status(302);
var now = Date.now();
while(Date.now() - now < 200) {}
res.end('Redirect');
})
app.get('/c', (req, res) => {
res.setHeader('location', 'http://localhost:8088/d');
res.status(302);
var now = Date.now();
while(Date.now() - now < 300) {}
res.end('Redirect');
})
app.get('/d', (req, res) => {
res.end(`
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Detail</h1>
<script type="text/javascript">
;(function() {
var timing = window.performance.timing;
console.log("navigationStart", timing.navigationStart);
console.log("unloadEventStart", timing.unloadEventStart);
console.log("unloadEventEnd", timing.unloadEventEnd);
console.log("redirectStart", timing.redirectStart)
console.log("redirectEnd", timing.redirectEnd)
console.log("fetchStart", timing.fetchStart)
console.log("responseEnd", timing.responseEnd)
console.log("domLoading", timing.domLoading)
})()
</script>
</body>
</html>
`);
})
navigationStart 1606036311534
unloadEventStart 1606036312099
unloadEventEnd 1606036317099
redirectStart 1606036311537
redirectEnd 1606036312089
fetchStart 1606036312089
responseEnd 1606036312093
domLoading 1606036317103
fetchStart == redirectEnd
;unloadEventEnd
比fetchStart
还要大,说明新页面请求时异步的。domLoading - responseEnd = 5010
:说明页面d
加载完毕后,需要等待上个页面的unload
事件处理函数执行完后才会开始解析新页面DOM树。
解析DOM树需要占用JS主线程,必须等待。而加载请求新页面资源则浏览器可以并行处理(也是浏览器的优化手段)。
7. domainLookupStart
DNS开始时间。
If a persistent connection is used, or the information is stored in a cache or a local resource, the value will be the same as
PerformanceTiming.fetchStart
.
一般情况下domainLookupStart
和fetchStart
存在时间差,但是当文档请求是个持久连接(划重点),或者信息已经被缓存(什么信息?),则两者一样。
7.1 keep-alive
持久化TCP
链接(persistent connection)。
res.setHeader('keep-alive', true);
当HTTP采用keepalive
模式,当客户端向服务器发生请求之后,客户端如何判断服务器的数据已经发生完成???
HTTP Keep-Alive模式
Keep Alive
HTTP/1.x 的连接管理
7.2 dns-prefetch
淘宝,JD等首页都使用了dns-prefetch
,如JD
<link rel="dns-prefetch" href="//m.360buyimg.com">
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//img10.360buyimg.com">
8. domainLookupEnd
9. connectStart
10. connectEnd
11 secureConnectionStart
12. requestStart
13. responseStart
14. responseEnd
15. domLoading
16. domInteractive
17. domContentLoadedEventStart
18. domContentLoadedEventEnd
19. domComplete
20. loadEventStart
21. loadEventEnd
指标
1. DNS解析时间
domainLookupEnd - domainLookupStart
8 Tips on How to Reduce DNS Lookups and Speed Them Up
2. TCP完成握手时间
connectEnd - connectStart
3. TTFB( Time To First Byte)
Time to first byte (TTFB) is a measurement used as an indication of the responsiveness of a webserver or other network resource.
Chrome DevTools里TTFB
的计算规则是responseStart - requestStart
但从Time_to_first_byte里描述的计算规则包含所有影响资源请求的时间,即TTFB = responseStart - navigationStart
How to Reduce TTFB to Improve WordPress Page Load Times
4. DOM树解析时间(可交互时间)
domLoaded = performance.timing.domContentLoadedEventEnd - performance.timing.domLoading
优化方向:Eliminate render-blocking resources
VConsole
性能数据
navigation = performance.timing.fetchStart - performance.timing.navigationStart
dns = performance.timing.domainLookupEnd - performance.timing.domainLookupStart
tcp = performance.timing.connectEnd - performance.timing.connectStart
request = performance.timing.responseEnd - performance.timing.requestStart
response = performance.timing.responseEnd - performance.timing.responseStart
domComplete (domLoaded)
domLoaded = performance.timing.domContentLoadedEventEnd - performance.timing.domLoading
domComplete = performance.timing.domComplete - performance.timing.domLoading
loadEvent = performance.timing.loadEventEnd - performance.timing.loadEventStart
total (DOM)
DOM= performance.timing.domComplete - performance.timing.navigationStart
total = performance.timing.loadEventEnd - performance.timing.navigationStart
Chrome DevTools Resource Timing API 数据
缓存一定很快吗?Chrome http缓存锁分分钟教你做人:node阻塞问题怎么解决
了解“URL输入完回车到页面展示的整个过程” ?
通过window.performance.timing
各个时间属性了解“URL输入完回车到页面展示的整个过程” ?
主要分为两个大阶段:清理当前页面和加载新页面
- 如果当前tab有页面,则开始
unload
当前页面
- 先触发
beforeunload
事件,等待事件处理函数执行完; - 如果
navigation
没有被取消,则触发unload
事件,等待事件处理函数执行完。
- DNS解析
- 建立TCP链接
- HTTPS的?
- 发送页面文档HTTP请求
- 接收到页面文档响应报文
- 开始解析DOM树(结合
document.readyState
的值变化)
document.readyState
改成loading
,并触发readystatechange
事件;- 构建过程【???】
- JS加载
- CSS加载
- 其他外部资源
- DOM构建完毕后
document.readyState
改成interactive
,并触发readystatechange
事件;- 开始加载
defer
的JS,加载完毕后触发DOMContentLoaded
事件。
- 页面其他外部执行加载完毕后
document.readyState
改成complete
,并触发readystatechange
事件;- 触发
window:load
事件;
Issues:
- 用户什么时候可以看到页面 ?