新聞中心
我問(wèn)你:“當(dāng)你從搜索引擎的結(jié)果頁(yè)面選擇打開(kāi)一條搜索結(jié)果時(shí),你覺(jué)得多長(zhǎng)時(shí)間之后,如果頁(yè)面還處于白屏或者沒(méi)有加載到關(guān)鍵信息,你會(huì)選擇關(guān)掉這個(gè)窗口?”

為淮北等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及淮北網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、淮北網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
《Designing for Performance》的作者 Lara Swanson 在2014年寫(xiě)過(guò)一篇文章《Web性能即用戶體驗(yàn)》,她在文中提到“網(wǎng)站頁(yè)面的快速加載,能夠建立用戶對(duì)網(wǎng)站的信任,增加回訪率,大部分的用戶其實(shí)都期待頁(yè)面能夠在2秒內(nèi)加載完成,而當(dāng)超過(guò)3秒以后,就會(huì)有接近40%的用戶離開(kāi)你的網(wǎng)站”。
Google和亞馬遜的研究表明,Google頁(yè)面加載的時(shí)間從0.4秒提升到0.9秒導(dǎo)致丟失了20%流量和廣告收入,對(duì)于亞馬遜,頁(yè)面加載時(shí)間每增加100毫秒就意味著1%的銷售額損失??梢?jiàn),頁(yè)面的加載速度對(duì)于用戶可能的下一步操作是多么的舉足輕重。
想一想,如果你希望你的網(wǎng)站在一秒鐘之內(nèi)呈現(xiàn)用戶想看的關(guān)鍵信息,有哪些可行的手段?Minify,壓縮,雪碧圖等等。
Google的Web性能工程師 Ilya Grigorik 會(huì)告訴你,你只需要理解瀏覽器的關(guān)鍵渲染路徑。
頁(yè)面性能可能是一個(gè)感性的東西
頁(yè)面的性能,看似是一個(gè)理性和量化的概念,實(shí)則也來(lái)自于用戶的感知,主觀的評(píng)價(jià),是一個(gè)偏感性的東西。
(參考自Google關(guān)鍵渲染路徑)
如果頁(yè)面可以做到優(yōu)先顯示與用戶操作有關(guān)的內(nèi)容,就可以讓用戶更快速的感知到操作得到響應(yīng),這個(gè)過(guò)程叫做“優(yōu)化關(guān)鍵渲染路徑”。
什么是關(guān)鍵渲染路徑
我記得,有一個(gè)非常經(jīng)典的面試題叫做:《當(dāng)瀏覽器地址欄輸入U(xiǎn)RL并回車后,發(fā)生了什么?》。
關(guān)鍵渲染路徑就是描述瀏覽器從收到 HTML、CSS 和 JavaScript 字節(jié)開(kāi)始,到如何使用HTML、CSS 和 JavaScript 在屏幕上渲染像素的中間過(guò)程。
如果我們能夠優(yōu)化這條路徑,就能讓頁(yè)面更快速的展示內(nèi)容,給用戶更好的體驗(yàn)。
全景圖
我們先嘗試站在高處,看一眼關(guān)鍵渲染路徑的全景圖,這樣能夠快速的領(lǐng)略一個(gè)大致輪廓和一些關(guān)鍵概念。
文檔對(duì)象模型 (DOM)
DOM概念之于Web開(kāi)發(fā)人員再熟悉不過(guò)了,當(dāng)瀏覽器發(fā)出請(qǐng)求并接收到HTML文檔后,它會(huì)有這樣一個(gè)流程來(lái)構(gòu)建DOM:字節(jié) → 字符 → 令牌 → 節(jié)點(diǎn) → 對(duì)象模型。
以下面這段代碼為例:
Critical Path Hello web performance students!
瀏覽器接收到HTML請(qǐng)求的返回結(jié)果,根據(jù)預(yù)定的流程解析HTML,文檔中的“開(kāi)標(biāo)簽”,比如,等會(huì)轉(zhuǎn)換成一個(gè)令牌(Token),然后令牌轉(zhuǎn)換成節(jié)點(diǎn)對(duì)象(Node)。
這個(gè)令牌解析并轉(zhuǎn)換為節(jié)點(diǎn)對(duì)象的過(guò)程,也是每個(gè)節(jié)點(diǎn)建立關(guān)系(樹(shù)形結(jié)構(gòu))的過(guò)程。例如:head的令牌出現(xiàn)在html令牌之后,但其閉標(biāo)簽出現(xiàn)在html閉標(biāo)簽之前,這就意味著head是html的子節(jié)點(diǎn),以此類推,建立節(jié)點(diǎn)的父子關(guān)系。
這個(gè)過(guò)程在瀏覽器中,叫做“Parse HTML”。
CSS 對(duì)象模型 (CSSOM)
當(dāng)DOM捕獲了頁(yè)面的內(nèi)容,我們還需要知道頁(yè)面如何展示這些內(nèi)容,所以需要構(gòu)建CSS 對(duì)象模型(CSSOM)。
瀏覽器解析DOM,遇到了link標(biāo)簽,發(fā)現(xiàn)它引用了一個(gè)外部樣式資源:style.css,于是瀏覽器會(huì)向外部請(qǐng)求樣式資源,然后進(jìn)行后續(xù)的DOM構(gòu)建工作。
CSS 被視為阻塞渲染的資源,這意味著瀏覽器將不會(huì)渲染任何已處理的內(nèi)容,直至 CSSOM 構(gòu)建完畢。
CSSOM有著一個(gè)和DOM構(gòu)建相似的流程:字節(jié) → 字符 → 令牌 → 節(jié)點(diǎn) → CSS對(duì)象模型。
以下面的CSS樣式為例,它會(huì)根據(jù)具體解析規(guī)則,將CSS文檔轉(zhuǎn)換成下面的樹(shù)形結(jié)構(gòu):
- body { font-size: 16px }
- p { font-weight: bold }
- span { color: red }
- p span { display: none }
- img { float: right }
這種樹(shù)形結(jié)構(gòu)讓CSS有層級(jí)繼承關(guān)系,子節(jié)點(diǎn)會(huì)繼承父節(jié)點(diǎn)的樣式。
前面談到CSS會(huì)阻塞瀏覽器的渲染過(guò)程,因?yàn)殇秩緲?shù)的構(gòu)建同時(shí)需要DOM和CSSOM,所以當(dāng)瀏覽器請(qǐng)求的style.css返回之后,瀏覽器會(huì)開(kāi)始解析樣式表,并重新計(jì)算樣式(Recalculate Style),將CSS轉(zhuǎn)換成CSSOM,然后進(jìn)行后續(xù)的操作。
值得注意的是,CSSOM運(yùn)算是一個(gè)非常復(fù)雜的過(guò)程,性能消耗會(huì)比較大,所以你會(huì)常常聽(tīng)到“老人們”說(shuō)寫(xiě)樣式“盡量使用class和id,保證層級(jí)扁平,減少過(guò)度層疊”,而且越是通用的CSS樣式,執(zhí)行速度越快,越是具體(選擇器)的CSS樣式,則執(zhí)行速度越慢。
DOM + CSSOM = 渲染樹(shù)
渲染樹(shù)和DOM樹(shù)不同,它只會(huì)捕獲一些頁(yè)面上可見(jiàn)的元素,比如,Header或display:none的元素不會(huì)放在渲染樹(shù)中。
渲染樹(shù)的構(gòu)建會(huì)從DOM的根節(jié)點(diǎn)開(kāi)始遍歷,對(duì)于不可見(jiàn)節(jié)點(diǎn)會(huì)忽略,然后在CSSOM中找到每個(gè)對(duì)應(yīng)節(jié)點(diǎn)的樣式規(guī)則并應(yīng)用,最后輸出的渲染樹(shù)會(huì)包含所有的可見(jiàn)內(nèi)容和樣式信息,如下圖:
布局和繪制
有了渲染樹(shù),瀏覽器會(huì)進(jìn)入布局和繪制階段。
布局就是弄清每個(gè)對(duì)象在頁(yè)面視窗(Viewport)上的確切大小和位置,它的輸出是一個(gè)“盒模型”,里面準(zhǔn)確的捕獲每一個(gè)元素在頁(yè)面視窗中的位置和尺寸。
在布局工作完成之后,瀏覽器會(huì)開(kāi)始繪制,將渲染樹(shù)轉(zhuǎn)換成屏幕上的像素,這樣,我們就能在瀏覽器中看到頁(yè)面的內(nèi)容。
短暫回顧一下“關(guān)鍵渲染路徑”的步驟
- 處理 HTML 標(biāo)記并構(gòu)建 DOM 樹(shù)
- 處理 CSS 標(biāo)記并構(gòu)建 CSSOM 樹(shù)
- 將 DOM 與 CSSOM 合并成一個(gè)渲染樹(shù)
- 根據(jù)渲染樹(shù)來(lái)布局
- 將各個(gè)節(jié)點(diǎn)繪制到屏幕上
當(dāng)DOM或者CSSOM發(fā)生變化的時(shí)候,瀏覽器就需要再次執(zhí)行一次上面的步驟。
JavaScript
到目前為止,我們還沒(méi)涉及到JavaScript,但它在整個(gè)關(guān)鍵渲染路徑中扮演著非常重要的角色,就如全景圖中畫(huà)的那樣,我們從一段簡(jiǎn)單的代碼開(kāi)始:
Hello web performance students!
- var span = document.getElementsByTagName('span')[0];
- span.textContent = 'javascript';
一個(gè)大家都知道的重要事實(shí)是:腳本在文檔的何處插入,就在何處執(zhí)行。
當(dāng)HTML解析過(guò)程中遇到一個(gè)script標(biāo)記時(shí),它會(huì)暫停DOM構(gòu)建,將控制權(quán)移交給JavaScript引擎,等JavaScript引擎運(yùn)行完畢,瀏覽器再?gòu)闹袛嗟牡胤交謴?fù)DOM構(gòu)建。也就是說(shuō),執(zhí)行內(nèi)聯(lián)的JavaScript會(huì)阻塞頁(yè)面的首次渲染。
現(xiàn)在我們假設(shè),這段JavaScript是外部資源。
Hello web performance students!
則瀏覽器的渲染會(huì)阻塞直到write.js的請(qǐng)求返回后,并執(zhí)行JavaScript后,繼續(xù)。
需要注意的是,在網(wǎng)頁(yè)中引入JavaScript腳本有一個(gè)微妙事實(shí),就是JavaScript不僅可以讀取和修改DOM屬性,還可以讀取和修改CSSOM屬性。
前面我們提到CSS是阻塞渲染的資源,當(dāng)它和JavaScript一起出現(xiàn)在頁(yè)面上時(shí),會(huì)發(fā)生這樣的事情:
Critical Path: Script Hello students!
在瀏覽器解析HTML構(gòu)建DOM過(guò)程中,發(fā)現(xiàn)了link標(biāo)簽,于是發(fā)出請(qǐng)求獲取style.css,然后繼續(xù)構(gòu)建DOM,此時(shí),它發(fā)現(xiàn)script標(biāo)簽,由于JavaScript可能會(huì)訪問(wèn)樣式屬性,所以它會(huì)阻止JavaScript的執(zhí)行直到styles.css返回并完成CSSOM構(gòu)建,然后執(zhí)行這一段JavaScript代碼,再繼續(xù)后面DOM的構(gòu)建和相關(guān)渲染操作。
于是styles.css的請(qǐng)求不僅阻塞后面的渲染,還阻塞了DOM的構(gòu)建。
如果將這段JavaScript作為外部資源,就是一個(gè)比較典型的頁(yè)面結(jié)構(gòu):
Critical Path Render Hello web performance
JavaScript和CSS資源請(qǐng)求是并行的,但仍然需要等到CSSOM構(gòu)建完成之后,JavaScript才可以執(zhí)行,然后在進(jìn)行后面的渲染工作。于是,當(dāng) DOM、CSSOM 和 JavaScript 執(zhí)行之間有大量的依賴關(guān)系時(shí),就很可能導(dǎo)致瀏覽器在處理及渲染網(wǎng)頁(yè)時(shí)出現(xiàn)延遲。
優(yōu)化策略
我們花了大量的篇幅來(lái)理解瀏覽器的渲染過(guò)程,理解DOM,CSSOM,渲染樹(shù),瀏覽器繪制,分析HTML,CSS和JS在渲染過(guò)程中的關(guān)系,我相信你已然受益匪淺,現(xiàn)在,我們來(lái)運(yùn)用這些知識(shí)加速你的網(wǎng)站。
第一步,分析你的網(wǎng)站渲染狀況
我們以Google為例,通過(guò)Chrome的Performance工具查看頁(yè)面渲染情況,如下圖,你應(yīng)該可以清晰的看到圖中有四條豎線,他們分別是什么含義呢?
(Google主頁(yè)的性能分析情況)
- 綠色豎線,代表First Paint,即瀏覽器開(kāi)始進(jìn)行像素的繪制
- 黃色豎線,代表First Meaningful Paint(首次有效繪制)用戶可以開(kāi)始看到部分內(nèi)容,但繪制仍在繼續(xù)
- 藍(lán)色豎線,代表大家比較熟悉的DOMContentLoaded
- 紅色豎線,代表load,頁(yè)面加載完成
優(yōu)秀的網(wǎng)站都能夠把“首次有效渲染”做到1秒之內(nèi)完成,這樣能夠讓用戶更快的看到所請(qǐng)求的頁(yè)面得到響應(yīng)。如果你的網(wǎng)站“首次有效渲染”超過(guò)1秒,那么就非常有必要重新分析一下網(wǎng)站的關(guān)鍵渲染路徑是否合理。
第二步,分析關(guān)鍵渲染路徑
在關(guān)鍵渲染路徑中,我們通常要關(guān)注三個(gè)點(diǎn):
- 頁(yè)面首次渲染需要的關(guān)鍵資源數(shù)量
- 關(guān)鍵資源的大小
- 關(guān)鍵渲染路徑的往返次數(shù)(Roundtrip)
我們的策略也非常簡(jiǎn)單,就是減少關(guān)鍵資源數(shù)量,降低資源大小,減少關(guān)鍵路徑的往返次數(shù)。
關(guān)鍵渲染的資源一般是阻止屏幕首次渲染HTML,CSS和JavaScript,所以最重要也是最難的部分的是你需要根據(jù)自己網(wǎng)站的實(shí)際情況分析,哪些是頁(yè)面繪制的所必須的,哪些是無(wú)關(guān)的。
第三步,根據(jù)分析采取優(yōu)化手段
(1) 減少關(guān)鍵資源的大小
我們首先從最簡(jiǎn)單也是最直接的減少關(guān)鍵資源的大小開(kāi)始:
對(duì)于所有的資源(HTML,JavaScript,CSS,Image等),你都應(yīng)該用上三大絕招:Minify,Compression和Cache,這里不過(guò)多的贅述里面的細(xì)節(jié)。
這一點(diǎn)對(duì)于HTML來(lái)說(shuō),非常關(guān)鍵,HTML作為渲染的關(guān)鍵資源,消除或者延遲加載肯定不太可能(這里指的是非局部渲染的關(guān)鍵HTML),能夠做到是消除無(wú)用代碼(比如:注釋)和最小化代碼(Minify)以及動(dòng)態(tài)局部渲染等。
(Google對(duì)頁(yè)面的HTML進(jìn)行了壓縮)
(2) 延遲JavaScript非阻塞資源加載
JavaScript和CSS都是阻塞渲染的資源,對(duì)于已經(jīng)鑒別出的對(duì)于首次渲染沒(méi)有起到關(guān)鍵作用的代碼,我們首先想到的是要延遲它的加載,讓它脫離關(guān)鍵渲染路徑。
首先,對(duì)于阻塞渲染的JavaScript,應(yīng)該將它放置在頁(yè)面body的底部,為什么呢?
JavaScript可以查詢和操作DOM和CSSOM,正如前面介紹的,HTML解析過(guò)程中構(gòu)建DOM,當(dāng)遇到JavaScript就停止DOM構(gòu)建執(zhí)行JavaScript,如果被執(zhí)行的JavaScript是放置在head附近,那么很可能要被操作或者查詢的DOM還沒(méi)有構(gòu)建到DOM當(dāng)中。
而對(duì)于,非阻塞渲染的JavaScript,我們應(yīng)該采用異步的方式加載,如下:


咨詢
建站咨詢