[聊一聊系列]聊一聊前端速度統計(性能統計)那些事兒

歡迎你們收看聊一聊系列,這一套系列文章,能夠幫助前端工程師們瞭解前端的方方面面(不只僅是代碼):javascript

https://segmentfault.com/blog/frontenddriverhtml

上一篇文章咱們討論了,如何進行前端日誌打點統計:前端

http://www.javashuo.com/article/p-xebcpebm-z.htmlhtml5

這一篇咱們來看看如何進行速度統計java

網站的速度影響了用戶訪問網站最初的體驗。試想,若是一個用戶,在等待了若干秒後,仍是停留在白屏的狀態,那麼他的選擇將是離開這個網站。性能統計有助於幫咱們檢測網站的用戶體驗。nginx

這裏引用百度百科中的一句話 ---- 一般一個網站,若是首屏時間在2秒之內是比較優秀的,5秒之內是能夠接受的,5秒以上就不可容忍了。用戶會選擇刷新頁面或馬上離開。segmentfault

這裏,有些數據須要與你們分享一下(來自FEX的統計):瀏覽器

產品 性能 收益
Google 延遲 400ms 搜索量降低 0.59%
Bing 延遲 2s 收入降低 4.3%
Yahoo 延遲 400ms 流量降低 5-9%
Mozilla 頁面打開減小 2.2s 下載量提高 15.4%
Netflix 開啓 Gzip性能提高 13.25% 帶寬減小50%

能夠看到,速度,對於一個網站來講,重要性可見一斑。服務器

一、網站都有哪些指標?

1.1 首屏時間


這個指標對於大多數網站來講,很是重要。那麼何爲首屏時間呢?引用百度百科裏的一句話,就是:網絡

網站用戶體驗的一個重要指標。 指一個網站被瀏覽器如IE窗口上部800*600的區域被充滿所需時間。

其實就是你的網頁剛進入時,渲染完整個瀏覽器屏幕的時間。

關因而否包含首屏全部的圖片下載完成。這個網上有些爭議,有的同窗說不包含圖片,只要DOM+樣式 都渲染完了,就算完成了。

筆者認爲,既然是首屏,那麼首屏上全部的東西都加載完成,讓用戶感覺不到還有沒完成的部分,就算完成了。

因此,綜合一下,我們的首屏時間,包括首屏的DOM元素的渲染,展示在用戶第一屏幕的全部圖片都完成。

其實Chrome提供開發着檢查網站整個渲染過程的小公舉,哦不,是小工具(如圖1.1.1所示)在F12開發者工具的Network面板裏面:

圖片描述
圖1.1.1

這個是屏幕捕獲的工具,能夠看到整個網頁的渲染過程。咱們接着來深究一下上述哪一個時間點是首屏時間點。

咱們來一塊兒看看百度首頁的首屏狀況,因爲百度首頁加載比較快,因此這裏我們模擬一下3G網的延遲(如圖1.1.2):
圖片描述
圖1.1.2

咱們看到,雖然在240ms的時候,網頁算是被渲染出來了,可是仍是有不少空白的地方。

279MS的時候,雖然框架都被渲染完成了,DOM與樣式也都渲染完成了,可是咱們看到圖片還不完整,因此,固然也不算首屏完成了。

有的同窗會說,318ms的時候,總算完成了吧,nonono,咱們向後觀察,就會發現還有一些元素會再被渲染出來。也就是說知道穩定以前,咱們都不能算首屏完成了。

知道487 ms的時候,頁面纔算加載完成了。而且以後不會再發生頁面的抖動了。

看完這些,相信聰明的你內心已經有數了,什麼是首屏時間。

1.2 白屏時間

    這個其實很少說,讀者也明白,就是頁面處於空白的時間。頁面空白,用戶就會焦躁,而且變得不耐心。影響白屏時間的多數是:DNS解析耗時+服務端耗時+網絡傳輸耗時。

1.3 用戶可操做時間

    顧名思義,這項指標值得是,咱們的網頁用戶可使用的時間。通常來說 domready時間,即是咱們的用戶可操做時間了。

1.4 總下載時間

    一般指,頁面整體的下載時間,全部的頁面資源都下載完成。

1.5 自定義指標

    因爲業務不一樣,站長們所關心的時間必然也不一樣了。好比你多是一個電商網站的站長,你關心你的第一屏商品到底展現的有多快( 一般這會帶來更多的收入),因此,你須要監控你的商品展示的時間。

2 如何統計本身網站的這些指標

若是並不想要花費精力在這些統計上,只是要小小的關注一下的話,固然能夠本身打開控制檯,在頁面的各個階段,將時間打印出來,亦或者是使用html5新增的接口:performance來評估一下本身的網站到底差在哪裏(如圖2.0.1)。
145325_AOW5_1177792.png
圖2.0.1

可是,你的測試並不能表明全部的用戶的狀況。並且,你須要一個監控程序,去時刻提醒着你,如今你的網站的速度處於什麼情況。

    因此,對於有追求一些的站長而言,筆者在這裏更建議採用用戶日誌,即,在本身網站的代碼中,增長統計,並把統計結果發送到服務器。在服務器採集這些日誌,併產生一個監控的網站。其實大可沒必要使用一些付費的服務,咱們本身就能夠輕輕鬆鬆的作一個簡答的速度監控服務。

    在本文的第三節,咱們將會一塊兒作一個小的速度監控服務的例子。不少站長甚至能夠拿過來直接使用。接下來,咱們仍是針對以前咱們提到過的幾個指標,注意講解統計方法:

2.1 如何統計首屏時間

    其實,對於網頁高度小於屏幕的網站來講,統計首屏時間很是的簡單,只要在頁面底部加上腳本打印當前時間便可,或者對於網頁高度大於一屏的網頁來講,只要在估算接近於一屏幕的元素的位置後,打印一下當前時間便可。

    好比,如今你有一個簡單的網頁(如圖2.1.1所示):

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
    </head>
    <body>
        <div>這是第一屏,這是第一屏</div>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <div>第一屏結尾,第一屏結尾</div>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
    </body>
</html>

152854_WBnH_1177792.png
圖2.1.1

咱們須要統計首屏時間的話,則須要定義一個基準,就是--何時用戶點開的當前網頁,HTML5的performance接口提供了這個時間:performance.timing.navigationStart,這個就是用戶訪問咱們網頁最開始的跳轉時間了。

<head>
    <script type="text/javascript">
        window.logInfo = {};
        window.logInfo.openTime = performance.timing.navigationStart;
    </script>
</head>

咱們在頁面開頭處,將基準記下。

接下來,在大約的首屏處加上咱們的統計:

<div>第一屏結尾,第一屏結尾</div>
<script type="text/javascript">
    window.logInfo.firstScreen = +new Date() - window.logInfo.openTime;
    console.log('首屏時間:', window.logInfo.firstScreen + 'ms');
</script>

咱們再來看看咱們的頁面(如圖2.1.2所示):
154049_1pEB_1177792.png
圖2.1.2

便有了首屏時間。

霸特,霸特。同窗們不要激動的太早。咱們這個首屏時間,並不沒有算上圖片。因此,咱們得把首屏中全部圖片的加載時間也算上。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
        <script type="text/javascript">
            window.logInfo = {};
            window.logInfo.openTime = performance.timing.navigationStart;
        </script>
    </head>
    <body>
        <div>這是第一屏,這是第一屏</div>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <div>第一屏結尾,第一屏結尾</div>
        <script type="text/javascript">
            (function logFirstScreen() {
                var images = document.getElementsByTagName('img');
                var iLen = images.length;
                var curMax = 0;
                var inScreenLen = 0;
                // 圖片的加載回調
                function imageBack() {
                    this.removeEventListener
                    && this.removeEventListener('load', imageBack, !1);
                    if (++curMax === inScreenLen) {
                        // 若是全部在首屏的圖片均已加載完成了的話,發送日誌
                        log();
                    }   
                }   
                // 對於全部的位於指定區域的圖片,綁定回調事件
                for (var s = 0; s < iLen; s++) {
                    var img = images[s];
                    var offset = {
                        top: 0
                    };
                    var curImg = img;
                    while (curImg.offsetParent) {
                        offset.top += curImg.offsetTop;
                        curImg = curImg.offsetParent;
                    }
                    // 判斷圖片在不在首屏
                    if (document.documentElement.clientHeight < offset.top) {
                        continue;
                    }
                    // 圖片尚未加載完成的話
                    if (!img.complete) {
                        inScreenLen++;
                        img.addEventListener('load', imageBack, !1);
                    }
                }
                // 若是首屏沒有圖片的話,直接發送日誌
                if (inScreenLen === 0) {
                    log();
                }
                // 發送日誌進行統計
                function log () {
                    window.logInfo.firstScreen = +new Date() - window.logInfo.openTime;
                    console.log('首屏時間:', window.logInfo.firstScreen + 'ms');
                }
            })();
        </script>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
    </body>
</html>

以上封裝的首屏時間函數,無依賴比較小巧,同窗們能夠直接使用於本身的項目中。這樣,咱們就輕輕鬆鬆的統計到了首屏時間。

2.2 如何統計白屏時間

    能夠在頁面的head底部添加的JS代碼來統計白屏時間,雖然這樣作可能並不十分精準,可是也能夠基本表明了首屏時間,如圖2.2.1所示。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
        <script type="text/javascript">
            window.logInfo = {};
            window.logInfo.openTime = performance.timing.navigationStart;
            window.logInfo.whiteScreenTime = +new Date() - window.logInfo.openTime;
            console.log('白屏時間:', window.logInfo.whiteScreenTime + 'ms');
        </script>
    </head>

155950_iY5p_1177792.png
圖2.2.1

2.3 如何統計用戶可操做時間

    前面提到過document.ready其實就能夠算做咱們的用戶可操做時間啦。咱們不妨直接試試,如圖2.3.1所示:

document
.addEventListener(
    'DOMContentLoaded',
    function (event) {
        window.logInfo.readyTime = +new Date() - window.logInfo.openTime;
        console.log('用戶可操做時間:', window.logInfo.readyTime);
    }
);

161009_jIOl_1177792.png
圖2.3.1

2.4 如何打印總下載時間

    頁面整體下載時間,使用window.onload便可,這能夠幫助咱們看看咱們全部的資源是否拖慢網頁,如圖2.4.1所示:

window.onload = function () {
    window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
    console.log('總下載時間:', window.logInfo.allloadTime + 'ms');
};

圖片描述
圖2.4.1

2.5 統一打印時間,更方便

    咱們將上述的統計合併,一個完整的統計就出來了,如圖2.5.1所示:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0">
        <script type="text/javascript">
            window.logInfo = {};
            window.logInfo.openTime = performance.timing.navigationStart;
            window.logInfo.whiteScreenTime = +new Date() - window.logInfo.openTime;
        </script>
    </head>
    <body>
        <div>這是第一屏,這是第一屏</div>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <div>第一屏結尾,第一屏結尾</div>
        <script type="text/javascript">
            (function logFirstScreen() {
                var images = document.getElementsByTagName('img');
                var iLen = images.length;
                var curMax = 0;
                var inScreenLen = 0;
                // 圖片的加載回調
                function imageBack() {
                    this.removeEventListener
                    && this.removeEventListener('load', imageBack, !1);
                    if (++curMax === inScreenLen) {
                        // 若是全部在首屏的圖片均已加載完成了的話,發送日誌
                        log();
                    }
                }   
                // 對於全部的位於指定區域的圖片,綁定回調事件
                for (var s = 0; s < iLen; s++) {
                    var img = images[s];
                    var offset = {
                        top: 0
                    };
                    var curImg = img;
                    while (curImg.offsetParent) {
                        offset.top += curImg.offsetTop;
                        curImg = curImg.offsetParent;
                    }
                    // 判斷圖片在不在首屏
                    if (document.documentElement.clientHeight < offset.top) {
                        continue;
                    }
                    // 圖片尚未加載完成的話
                    if (!img.complete) {
                        inScreenLen++;
                        img.addEventListener('load', imageBack, !1);
                    }
                }
                // 若是首屏沒有圖片的話,直接發送日誌
                if (inScreenLen === 0) {
                    log();
                }
                // 發送日誌進行統計
                function log () {
                    window.logInfo.firstScreen = +new Date() - window.logInfo.openTime;
                }
            })();
            document
            .addEventListener(
                'DOMContentLoaded',
                function (event) {
                    window.logInfo.readyTime = +new Date() - window.logInfo.openTime;
                }
            );
            window.onload = function () {
                window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
                var timname = {
                    whiteScreenTime: '白屏時間',
                    firstScreen: '首屏時間',
                    readyTime: '用戶可操做時間',
                    allloadTime: '總下載時間'
                };
                for (var i in timname) {
                    console.log(timname[i] + ':' + window.logInfo[i] + 'ms');
                }
            };
        </script>
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
        <img src="http://static.oschina.net/uploads/space/2016/0623/152644_6UUC_1177792.png">
    </body>
</html>

161744_kOfK_1177792.png
圖2.5.1

按照上一節( http://www.javashuo.com/article/p-xebcpebm-z.html )所說,咱們將這個日誌發送到服務端去。

var logStr = '';
for (var i in timname) {
    logStr += '&' + i + '=' + window.logInfo[i];
}
(new Image()).src = 'http://localhost:8091/?action=speedlog' + logStr;

開啓咱們的nginx監聽,並在服務端接收這條日誌如圖2.5.2,圖2.5.3所示:

162439_crrp_1177792.png
圖2.5.2

162553_ZBUv_1177792.png
圖2.5.3

咱們看看日誌裏面是否是已經多了一條了呢?

要是再加以定時任務,日誌採集等功能的輔助,咱們就能實時掌握本身網站的性能啦。

不要走開,請關注我。下一章,咱們將繼續聊聊百度移動版首頁那些事。

http://www.javashuo.com/article/p-wrkmdljz-gd.html

後續,咱們也會一塊兒來聊聊,如何優化咱們的這些速度,以提升咱們的網站性能。

原創文章,版權全部,轉載請註明出處

相關文章
相關標籤/搜索