yaofly2012 / note

Personal blog
https://github.com/yaofly2012/note/issues
44 stars 5 forks source link

WebAPI: performance #191

Open yaofly2012 opened 4 years ago

yaofly2012 commented 4 years ago

image

  1. Attributes underlined may not be available in navigation involving documents from different origins

全链路页面性能 image

通过这个过程理解浏览器工作流程。

Performance

当前页面的性能相关数据。

  1. Performance Timeline API
  2. High Resolution Time API
  3. Navigation Timing API
  4. User Timing API
  5. Resource Timing API

PerformanceTiming

1. navigationStart

当前浏览器tab里上个页面确定unload(beforeunload之后),开始处理当前页面的时间点(毫秒,时间戳)。

2. unloadEventStart

3. unloadEventEnd

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的区别两者之间夹杂着redireactStartredireactEnd。 如果存在重定向(即redireactStartredireactEnd非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

  1. fetchStart == redirectEnd
  2. unloadEventEndfetchStart还要大,说明新页面请求时异步的。
  3. 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.

一般情况下domainLookupStartfetchStart存在时间差,但是当文档请求是个持久连接(划重点),或者信息已经被缓存(什么信息?),则两者一样。

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:

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 image

但从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性能数据

System面板的性能数据 image

  1. navigation = performance.timing.fetchStart - performance.timing.navigationStart

  2. dns = performance.timing.domainLookupEnd - performance.timing.domainLookupStart

  3. tcp = performance.timing.connectEnd - performance.timing.connectStart

  4. request = performance.timing.responseEnd - performance.timing.requestStart

  5. response = performance.timing.responseEnd - performance.timing.responseStart

  6. domComplete (domLoaded)

    • domLoaded = performance.timing.domContentLoadedEventEnd - performance.timing.domLoading
    • domComplete = performance.timing.domComplete - performance.timing.domLoading
  7. loadEvent = performance.timing.loadEventEnd - performance.timing.loadEventStart

  8. 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输入完回车到页面展示的整个过程” ? 主要分为两个大阶段:清理当前页面加载新页面

  1. 如果当前tab有页面,则开始unload当前页面

    • 先触发beforeunload事件,等待事件处理函数执行完;
    • 如果navigation没有被取消,则触发unload事件,等待事件处理函数执行完。
  2. DNS解析

  3. 建立TCP链接

    • HTTPS的?
  4. 发送页面文档HTTP请求

  5. 接收到页面文档响应报文

  6. 开始解析DOM树(结合document.readyState的值变化)

    • document.readyState改成loading,并触发readystatechange事件;
    • 构建过程【???】
    • JS加载
    • CSS加载
    • 其他外部资源
  7. DOM构建完毕后

    • document.readyState改成interactive,并触发readystatechange事件;
    • 开始加载defer的JS,加载完毕后触发DOMContentLoaded事件。
  8. 页面其他外部执行加载完毕后

    • document.readyState改成complete,并触发readystatechange事件;
    • 触发window:load事件;

Issues:

  1. 用户什么时候可以看到页面 ?

参考

  1. Window.performance
yaofly2012 commented 4 years ago

Document.readyState

一、概述

标记文档的加载状态,并且当状态发生变化时会触发readystatechange事件。Document.readyState有3个值:

  1. loading
  2. interactive
  3. complete

二、取值

1. loading

表示DOM正在解析中

2. interactive

盗图:domInteractive image

表示DOM树已经构建完毕,可以访问并操作document对象了,并且直接(JS脚本)或间接(CSS资源)阻塞DOM树构建的外部资源也已经执行完毕。 但是异步的外部资源可能还在加载中,包含: