從微博的改版談網(wǎng)頁(yè)重構(gòu)
一、HTTP請(qǐng)求數(shù)的權(quán)衡
1、為什么要關(guān)心http請(qǐng)求?
當(dāng)瀏覽器向Web服務(wù)器發(fā)出請(qǐng)求時(shí),它向服務(wù)器傳遞了一個(gè)數(shù)據(jù)塊,也就是請(qǐng)求信息。在用戶打開(kāi)一個(gè)頁(yè)面的初初,包括等待時(shí)間、請(qǐng)求時(shí)間、建立響應(yīng)時(shí)間、渲染時(shí)間……,都是消耗在前端的。比如下載圖片、下載樣式表、JavaScript腳本、flash等文件。大家應(yīng)該都經(jīng)歷過(guò)那個(gè)“多圖殺貓”的時(shí)代,加載那樣一個(gè)網(wǎng)頁(yè)會(huì)花費(fèi)大量的時(shí)間。減少這些資源文件的請(qǐng)求數(shù)將是提高網(wǎng)頁(yè)顯示效率的重點(diǎn)。
假設(shè)用戶家的網(wǎng)速是1M/秒,那么他打開(kāi)一個(gè)網(wǎng)頁(yè)時(shí),如果網(wǎng)頁(yè)文件小于1M,理論上他可以在一秒之內(nèi)打開(kāi)網(wǎng)頁(yè)。下載網(wǎng)頁(yè)的快慢在顯示速度上占了很大比重,所以,網(wǎng)頁(yè)本身體積越小,瀏覽速度就會(huì)越快。這就需要產(chǎn)品、交互、設(shè)計(jì),從最初就遵循盡量精簡(jiǎn)的原則。
現(xiàn)在,就揭開(kāi)新版微博的面紗,看看微博3.0和新版微博的區(qū)別吧。
微博3.0是大家熟悉的兩欄結(jié)構(gòu),總寬為800px,有一級(jí)導(dǎo)航和二級(jí)導(dǎo)航、發(fā)布框、feed區(qū)、個(gè)人簡(jiǎn)介區(qū)。新版微博是現(xiàn)在最流行的三欄式布局,總寬950px,除以前的內(nèi)容一個(gè)都不少之外,還整合出了左側(cè)導(dǎo)航和右側(cè)各種引導(dǎo)和公告。所以從理論上講,雖然內(nèi)容更豐富了,但新版微博著實(shí)比微博3.0的頁(yè)面體積大了很多。
2、什么是bigpipe?
網(wǎng)上有個(gè)例子舉得好:在飯館點(diǎn)菜吃的時(shí)候,如果點(diǎn)了四個(gè)菜,廚師沒(méi)有必要把四個(gè)菜一起炒好再上來(lái)。微博3.0就是這種把所有菜都炒好再上桌的網(wǎng)頁(yè)加載模式。所以用戶吃上菜的時(shí)候,已經(jīng)是第5秒了。現(xiàn)在新版微博的bigpipe網(wǎng)頁(yè)加載模式,是炒好一個(gè)菜先一個(gè)菜,用戶可以先吃著,廚師再炒第二個(gè)菜。甚至可以幾個(gè)菜并發(fā)同時(shí)炒。所以用戶吃上第一口菜的時(shí)間可能是第1秒,比之前提前了很多。
JS工程師把頁(yè)面分割成若干個(gè)小塊(pagelet),模塊彼此獨(dú)立,把html語(yǔ)言轉(zhuǎn)變?yōu)镴S語(yǔ)言,再把CSS通過(guò)style的方式加載進(jìn)這段代碼,而不需要用以往的頭部link css地址的方式取樣式。每個(gè)模塊有自己對(duì)應(yīng)的html、CSS、JS,一旦開(kāi)始運(yùn)行模塊,就會(huì)尋找到對(duì)應(yīng)的CSS,并顯示對(duì)應(yīng)innerHTML內(nèi)容插入到對(duì)應(yīng)的html元素中,同時(shí)渲染出本模塊效果。比如執(zhí)行到feed區(qū)域的 id=“pl_content_homeFeed”時(shí),樣式表只調(diào)用了feed.css。
3、為什么新版微博 CSS的HTTP請(qǐng)求數(shù)不降反增?
通過(guò)上面說(shuō)的這種模式,css被全部link到頭部,是為了給后臺(tái)代碼提供出pagelet所需要的樣式列表。以前微博3.0頭部只link了3個(gè)CSS,新版微博首頁(yè)頭部卻需要link10多個(gè)css。雖然加載文件多了,新版微博CSS加載請(qǐng)求數(shù)反而高于v3,看似yslow降級(jí)了(這個(gè)數(shù)據(jù)已經(jīng)不能說(shuō)明任何問(wèn)題了)。但實(shí)際上新版微博CSS沒(méi)有像以往一樣合并起來(lái),而是用一個(gè)加載一個(gè),CSS和JS被分配到不同流水線中,模塊的加載變成并行的,先執(zhí)行完的模塊先顯示出來(lái)。所以新版微博CSS渲染的總時(shí)間并不超過(guò)V3CSS渲染的總時(shí)間,速度反而快了很多,減少了視覺(jué)等待。
上面這張表格,來(lái)自yslow的分析。我們通過(guò)把頁(yè)面切成細(xì)小模塊寫(xiě)樣式的做法,雖然請(qǐng)求數(shù)比以前大了8倍,但總大小上直降20K。
將多個(gè)CSS合并的做法固然可以減少請(qǐng)求,但對(duì)上億用戶的微博頁(yè)面來(lái)說(shuō),完成合并也許會(huì)帶來(lái)5%速度的提升。但是如果按bigpipe模式,即使http請(qǐng)求數(shù)提高了,但是整體的速度也許是之前的50%。
二、對(duì)CSS的優(yōu)化處理
1、提取公用模塊或公用元素,并反復(fù)調(diào)用
如果用戶每次訪問(wèn)微博首頁(yè)時(shí),就重新下載讀取CSS文件。這就會(huì)造成給服務(wù)器帶來(lái)額外壓力且用戶重復(fù)讀取耗時(shí)。新版微博的做法是,把模塊分為全局級(jí)模塊和頁(yè)面級(jí)模塊,首頁(yè)是全站的核心,所有模塊都是重要且重復(fù)性高的,所以首頁(yè)的所以模塊都是全局級(jí)模塊。首頁(yè)所需要的CSS被整理成一個(gè)pl列表反饋給工程師,等待處理。而一些非公共的css模塊樣式被單獨(dú)寫(xiě)在屬于本頁(yè)面的文件里,這樣就最大化的節(jié)省了文件大小及利用率。這么做還有一個(gè)好處,就是公共模塊樣式被調(diào)用過(guò)后,會(huì)在瀏覽器里留下緩存。調(diào)用最頻繁的模塊,反而樣式被最快的加載進(jìn)來(lái)。
舉個(gè)簡(jiǎn)單的例子:
.clearfix:after{content: ".";display: block;height: 0;clear: both;visibility: hidden;}
.clearfix {display: inline-block;}
這是一段全局代碼,基本上每個(gè)頁(yè)面都會(huì)用到這個(gè)類(lèi),我們就沒(méi)必要把這句寫(xiě)在每個(gè)網(wǎng)頁(yè)的CSS里面,只把它提取到base.css里,并方便進(jìn)行皮膚管理。
又比如,首頁(yè)右側(cè)欄有個(gè)“可能感興趣的活動(dòng)”類(lèi)似的模塊都是采用獨(dú)立的div容器,這個(gè)段落的詳細(xì)代碼,如果寫(xiě)在公用CSS文件里,肯定就浪費(fèi)了。
2、盡可能少的使用css images
能通過(guò)代碼或字符實(shí)現(xiàn)的,就不用圖片去解決。比如“可能感興趣的人”展開(kāi)氣泡上下三角、返回頂部的箭頭、“更多”后面的»符號(hào)等。既減小CSS圖片請(qǐng)求,又不會(huì)面臨若干套皮膚升級(jí)困難的問(wèn)題,僅通過(guò)對(duì)CSS的color、backgroud等屬性的控制,就可以換色了。
可以看看按這個(gè)做法之后明顯的優(yōu)勢(shì),下圖來(lái)自yslow的statistics。微博3.0的css image總大小為970.1K,新版微博的css image總大小為693.9K,總量直降30%。
3、盡量使用CSS3等新技術(shù)
在新版微博里,我們制定了使用CSS3的原則,即非圖片類(lèi)的元素效果圖,如圓角、陰影、漸變、半透等效果,可以通過(guò)樣式控制,而無(wú)需切圖的元素,在得到設(shè)計(jì)師認(rèn)可后,不用圖片,只做樣式控制。滿足高級(jí)瀏覽器的視覺(jué),ie系列不能顯示的,有原則的放棄。不僅為速度助力,還在放棄低級(jí)瀏覽器的大方向前進(jìn)一步。
4、鼠標(biāo)滑上效果改用偽類(lèi)實(shí)現(xiàn)
在逐步放棄ie6的事情上,新版微博已經(jīng)盡最大的努力做了。為了保證各瀏覽器的完全兼容,微博3.0以前我們?cè)?jīng)放棄讓CSS實(shí)現(xiàn)鼠標(biāo)滑上效果,而由JS控制。隨著ie6使用率的日益降低,新版微博又一大革新就是重新使用偽類(lèi),僅通過(guò)CSS就實(shí)現(xiàn)的瀏覽器原生效果,不僅計(jì)算速度比計(jì)算一個(gè)JS快得多,也終于放棄了低端的ie6
每個(gè)單條feed在鼠標(biāo)滑上時(shí),都會(huì)顯示舉報(bào)和刪除鏈接。這是交互設(shè)計(jì)出于對(duì)頁(yè)面呈現(xiàn)內(nèi)容的視覺(jué)舒適感所做的設(shè)計(jì),我們通過(guò)對(duì)塊元素直接寫(xiě)偽類(lèi),實(shí)現(xiàn)這個(gè)效果,不需要再通過(guò)JS了。ie6呢?就讓它一直擺著去吧。
三、對(duì)dom結(jié)構(gòu)的優(yōu)化處理
1、bigpipe模式重構(gòu)并優(yōu)化垃圾代碼
v2從v1來(lái),v3從v2來(lái),在v3不堪重負(fù)的時(shí)候,新版微博的代碼優(yōu)化誓在必行了。所以我們并沒(méi)有沿用之前的結(jié)構(gòu)和CSS,而是直接推翻v3,重構(gòu)新版微博。和JS工程師一起搭建的bigpipe模式,把頁(yè)面分成細(xì)小的塊,每一個(gè)模塊對(duì)應(yīng)一個(gè)CSS。代碼寫(xiě)到最優(yōu),結(jié)構(gòu)和樣式完全分離,并杜絕內(nèi)聯(lián)調(diào)用的方式。下圖示意了我們用模塊配頁(yè)面的最終效果,模塊可以被細(xì)分為如此程度。模塊拆的細(xì),復(fù)用性被提高。
2、盡量減少代碼體積
由于代碼行數(shù)越少體積就越小,所以我們這次想辦法減少網(wǎng)頁(yè)代碼的行數(shù)。相同或類(lèi)似的模塊,說(shuō)服設(shè)計(jì)師把視覺(jué)規(guī)范統(tǒng)一。我們只通過(guò)對(duì)CSS補(bǔ)丁,覆蓋原樣式,并不改變頁(yè)面的dom結(jié)構(gòu),直接降低重復(fù)代碼率。舉個(gè)例子,“我的首頁(yè)”和“我的profile頁(yè)”,同樣是有feed區(qū)域的,區(qū)別是但一個(gè)有頭像,一個(gè)沒(méi)有頭像。只需要一套feed.css代碼,然后在“我的profile頁(yè)”獨(dú)立的頁(yè)面級(jí)CSS中,打個(gè)去掉頭像的補(bǔ)丁即可。
3、首頁(yè)中杜絕Table布局和iframe
杜絕首頁(yè)中出現(xiàn)Table布局。因?yàn)閭鹘y(tǒng)的table布局,是把內(nèi)容全部加載完成后,才渲染樣式,延遲效果嚴(yán)重。而iframe頁(yè)面框架,是非語(yǔ)義的,即使為空也會(huì)有較大資源消耗,還會(huì)阻止頁(yè)面的onload。
四、對(duì)圖片的優(yōu)化處理
1、圖片的存儲(chǔ)格式
我們改變了v3的做法,把icon類(lèi)小圖片或背景類(lèi)圖片,由以前的gif存儲(chǔ)盡可能多的轉(zhuǎn)為png8的存儲(chǔ),這是個(gè)減小圖片體積的好辦法。Png8有g(shù)if的所有特點(diǎn),但是相比gif,png8的優(yōu)勢(shì)是alpha透明和更優(yōu)的壓縮。png24全透明的圖片,只給支持的瀏覽器使用,ie6在不影響視覺(jué)的前提下,改為gif呈現(xiàn)。我們還會(huì)利用的圖片優(yōu)化工具處理圖片,保證效果但卻降低文件大小。下圖是主鍵類(lèi)頁(yè)面的images文件夾示意圖,除必須獨(dú)立的icon外,png類(lèi)型的圖片比重大得多。這在之前的V3并沒(méi)有做到。
2、css sprites
在圖片的拼合方面,我們是持之以恒這么做的。在v3里,我們把所有首頁(yè)和profile頁(yè)里出現(xiàn)的背景類(lèi)圖片都拼合到一張大圖上,新版微博比之前高明在,我們把放置文件夾細(xì)分。假設(shè)我們把公用型放入common,頁(yè)面類(lèi)放入index,換膚類(lèi)放入skin。把sprites拆分的更細(xì),盡可能在加載首頁(yè)時(shí),減少圖片請(qǐng)求數(shù)。
3、大圖片、小圖片
對(duì)于大背景圖,我們的做法是不分割成小區(qū)塊兒的。首先的因?yàn)椋疽粋€(gè)圖片的請(qǐng)求數(shù)會(huì)變成多個(gè)。另外,大約80%以上調(diào)用圖片所消耗的時(shí)間,是用來(lái)檢索緩存和確定鏈接是否有效的阻塞時(shí)間。也就是說(shuō),如果把一個(gè)大圖片,切成若干小圖片,雖然解決了圖片的加載時(shí)間,卻花費(fèi)了更多的阻塞時(shí)間。
4、在已知寬高的圖片標(biāo)簽內(nèi),直接指定寬高。
Feed區(qū)域里頭像需求是50*50的,所以后臺(tái)直接吐出這個(gè)尺寸的圖片。在已知寬高的情況的,我們?cè)趇mg標(biāo)簽中直接指定了height和width參數(shù)。因?yàn)槿绻麨g覽器沒(méi)有找到這兩個(gè)參數(shù),它需要一邊下載圖片一邊計(jì)算大小,首頁(yè)有多少條feed就有多少個(gè)頭像,瀏覽器需要不斷地調(diào)整計(jì)算。當(dāng)瀏覽器知道了height和width參數(shù)后,即使圖片暫時(shí)無(wú)法顯示,頁(yè)面上也會(huì)預(yù)留空位,繼續(xù)加載后面的內(nèi)容。
新版上線后,不少公測(cè)用戶的反饋速度變快了,微博瀏覽起來(lái)更順暢了。這不是我們頁(yè)面重構(gòu)組的功勞,是整個(gè)微博團(tuán)隊(duì),或者說(shuō)是bigpipe思想的功勞。但“速度快了”這句話本身,就是對(duì)我們團(tuán)隊(duì)工作最好的褒獎(jiǎng),對(duì)我們參與開(kāi)發(fā)的團(tuán)隊(duì)成員的最好的鼓勵(lì)。抓住產(chǎn)品改版的機(jī)會(huì),就是自己對(duì)優(yōu)化的研究和經(jīng)驗(yàn)的積累落實(shí)到細(xì)節(jié)的機(jī)會(huì)。雖然還任重道遠(yuǎn)。