歡迎你們收看聊一聊系列,這一套系列文章,能夠幫助前端工程師們瞭解前端的方方面面(不只僅是代碼):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
不要走開,請關注我。下一章,咱們將繼續聊聊前端存儲那些事兒。
原創文章,版權全部,轉載請註明出處