聊一聊百度移動端首頁前端速度那些事兒

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

http://my.oschina.net/MrHou/blog?catalog=477313&temp=1466755903794php

這一期,我們一塊兒來聊一聊----百度移動端首頁前端速度的那些事兒css

1 長什麼樣?
    咱們的業務就是 https://m.baidu.com
    別覺得只有一個搜索框,咱們還有下面豐富的卡片內容,能夠提供各式各樣的服務。如圖1.1

                                        圖1.1
其實整個頁面的邏輯相對是比較複雜的。
還有各式各樣的卡片,輕輕下拉,便可看到,如圖1.2

                                            圖1.2html

2 面臨的挑戰
    可能代碼的量級沒有不少webapp恐怖,但是「百度首頁要秒開」倒是一個共識,能夠看到(如圖2.1),在利用上了緩存的狀況下,咱們的首頁包大小gzip後只有11.1k左右。耗時也就是500多毫秒。大部分用戶「秒開」不是事兒。

                                                                                        圖2.1前端

可是,咱們的業務在不斷的增加的同時,要維持這樣的包大小,就是一門藝術了。
要快,可是咱們的服務也必須萬無一失,(後續我會分享百度移動端首頁的前端架構設計)那麼這樣的優化,是如何作到的呢,又如何兼顧穩定性,架構性,與速度呢?別急,讓咱們把這些優化一一道來。java

3 咱們的業務與咱們的優化
3.1 靜態文件在哪裏?
    爲了求快,首頁是沒有js和css外鏈的,這樣會再發起屢次請求,相信對於各位前端小能手來講,也是老生常談的前端優化了。因此,整個首頁渲染出來,只須要一次請求(除了iconfont)。其餘的首屏所須要的js與css,所有在上線前,編譯時,編譯內聯至HTML中,如圖3.1.1。web


                                                                                            圖3.1.1緩存

3.2 緩存!緩存!
    然而,首頁並不知足於此,首頁的不少樣式和腳本,須要在同步的時候就初始化,可是,若是每次都傳輸一些不變的靜態文件或者html,實在是太浪費了,若是html/css/js一直不變,那直接緩存到客戶端不就行了。
    因而首頁的第二項優化,就展現了威力,localstorage,關於這個客戶端存儲,陌生的同窗能夠查一查。也能夠直接閱讀我接下來要寫的聊一聊系列文章。
    咱們把不變的js/css/html所有存儲到localstorage中去,下次加載首頁的時候。在特定的位置,沒必要再從服務端把特定位置的js/css/html傳過來。只須要傳一句話----"<script>readlocalstorage();</script>"就行。
至於存儲的方法,例子以下:服務器

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <div data-local="test1">
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
            這部份內容很是多將會緩存起來
        </div>
        <script>
            function cacheOne(attrid) {
                var content = document.querySelector('[data-local="' + attrid + '"]').outerHTML;
                localStorage.setItem(attrid, content);
            }
            cacheOne('test1');
        </script>
    </body>
</html>

咱們將html的內容存儲到了localstorage中,如圖3.2.1cookie


                                                                                    圖3.2.1
下次,再訪問的時候,咱們使用服務端把緩存起來的html不傳送,而是隻傳送讀取相關的js,如圖3.2.2

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <script type="text/javascript" data-local="test1">
            function readOne(attrid) {
                var content = localStorage.getItem(attrid);
                document.querySelector('[data-local="' + attrid + '"]').outerHTML = content;
            }
            readOne('test1');
        </script>
    </body>
</html>

                                                                                圖3.2.2
咱們看到,雖然展現內容相同,可是第二次傳輸的時候,頁面的量明顯減少。並且使用這種方式咱們使用的地方越多,這種優點就越明顯。

百度移動端首頁的不少css/html/js就是這樣緩存在客戶端的。

有同窗會說,那麼如何知道何時該傳讀local,何時該傳寫local呢?
很簡單,咱們在寫入local的時候,同時在cookie中種下當前全部要緩存的內容的版本(MD5戳)就行。
由於cookie是會在同步訪問的時候,傳送到服務端的,而local不會,因此,咱們在服務端決定要傳送內容,仍是傳送讀取local代碼。就靠咱們種下的cookie了。咱們在這裏,使用php來作一個實驗:

<!DOCTYPE HTML>
    <html>
        <head>
            <meta charset="utf-8"/>
        </head>
        <body>
            <?php $curversion='1'?>
            <?php if ($_COOKIE['localversion'] !== $curversion) {?> 
            <div data-local="test1">
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
                這部份內容很是多將會緩存起來
            </div>
            <script>
                function cacheOne(attrid) {
                    var content = document.querySelector('[data-local="' + attrid +'"]').outerHTML
    ;
                    localStorage.setItem(attrid, content);
                }
                cacheOne('test1');
                document.cookie="localversion=<?php echo $curversion?>;";
            </script>
            <?php } else {?>
                <script type="text/javascript" data-local="test1">
                    function readOne(attrid) {
                        var content = localStorage.getItem(attrid);
                        document.querySelector('[data-local="' + attrid + '"]').outerHTML = content
    ;
                    }
                    readOne('test1');
                </script>
            <?php }?>
        </body>
    </html>

咱們在php中判斷,若是cookie中有version,證實種過cookie,寫過local,因此,不用傳內容了,直接傳script就行了,若是沒有就要傳輸而且寫入。咱們能夠看到效果,一樣的頁面,第一次訪問的時候,內容大小是1.6K,如圖3.2.3

                                                                        圖3.2.3
再次刷新的時候,內容量已經減少到了474b,如圖3.2.4。


                                                                                    圖3.2.4

若是用戶的cookie和local不一致怎麼辦,若是用戶不支持local怎麼辦?這些疑問其餘讀者自行思考一下(其實很簡單)。

3.3 外鏈!外鏈!
    畢竟業務在增大,光首屏的那些css/js/html已經沒法知足咱們了。咱們須要更多的腳本,更多的css。你可能會很輕鬆的想到,外鏈引入唄。可是,通過調研,咱們發現移動端的文件緩存率很是的低(大約30%左右)。也就是說移動端的緩存環境是很是殘酷的。因此,咱們又開始了極限優化。咱們將全部的js/css等靜態文件,經過一個接口所有返回。如圖3.3.1


                                                                                        圖3.3.1
這樣能夠達到合併外鏈請求的目的,咱們又將這些靜態文件,也一一緩存到localstorage中,如圖3.3.2:


                                                                                    圖3.3.2
每一個文件以本身文件內容生成的版本號爲戳,標識本身的惟一性。每次服務端返回頁面時,會把當前在服務器上的全部靜態文件版本號,返給前端,如圖3.3.3


                                                                                            圖3.3.3
前端首屏加載完成後,會用這些版本號與local中進行一一對比,發現不一致的js/css,會一塊兒發送一個合併請求。如圖3.3.1所示。這樣能夠保證每一個文件的緩存與版本迭代。同時,也避免了過多的外鏈。

3.4 DOM也緩存
咱們的模板和數據,也會被緩存至localstorage中,有同窗可能會問,那什麼東西不緩存?答案就是,變化的東西,若是有部分html與數據在刷新的時候會常常性的變更的話,這種緩存方式就失去了它的意義。如圖3.4.1


                                                                                    圖3.4.1

3.5 使用iconfont
    因爲咱們的不少業務是不須要多彩色圖的,因此這個時候,iconfont就派上了用場,在知足UE高清的需求下,能夠節省大量的資源。如圖3.5.1,這些icon就可使用iconfont。咱們的全部icon也是編譯在同一個font文件中的。

                                                    圖3.5.1

3.6 卡片的異步加載與緩存
    隨着咱們的業務愈來愈多,咱們提供服務的卡片也愈來愈多了,可是!!!依舊不能影響咱們首頁的速度。咱們又開始了極限優化。首先,咱們首屏也就須要2張卡片,按照市售手機的尺寸來看。咱們兩張卡片足夠填充滿首屏了。特殊狀況,咱們會有特殊處理(針對大屏幕手機),在用戶下拉的時候,再去加載更多的卡片。這樣能夠節省用戶流量,還可以提高首頁速度。接下來,咱們如法炮製,也將卡片內容(html/css/js)存儲到了local中。異步拉取卡片的時候,若是卡片內容沒有變。服務端就不要返回了。

3.7 不在首屏的就要異步化!
咱們有不少用戶功能,用戶不必定每次都會用,若是上來就開始加載,必然會浪費速度與流量,因而,咱們將一些「第二步操做」,只有在觸發時纔會進行加載。這樣,保證了按需加載。
如,咱們的管理頁面功能,是個點擊才能進入的浮層,對於這種功能設計,就能夠採用按需加載,而不是伴隨首頁一塊兒加載,如圖3.7.1


                                                        圖3.7.1

3.8 少許靜態文件的域名

咱們的logo與iconfont均是放在m.baidu.com域下的,這樣節省了DNS的解析。雖然可能帶來的問題是,發送圖片請求的時候,會攜帶cookie。可是咱們的cookie也是極少的。這點上性能仍是有所提高的。如圖3.8.1咱們的logo就是在m.baidu.com域名下:

                                                                                    圖3.8.1

3.9 極小的圖片base64化

對於小於1k的圖片,咱們將其變爲base64編碼,並融入到css中,一塊兒緩存到localstorage中去,這樣即節省了網絡請求,同時使圖片也能夠緩存到local中去了。

                                                                                                    圖3.9.1

 

不要走開,請關注我。下一章,咱們將繼續聊聊前端存儲那些事兒。

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

相關文章
相關標籤/搜索