目錄
每個參與過開發(fā)企業(yè)級web應(yīng)用的前端工程師或許都曾思考過前端性能優(yōu)化方面的問題。我們有雅虎14條性能優(yōu)化原則,還有兩本很經(jīng)典的性能優(yōu)化指導(dǎo)書:《高性能網(wǎng)站建設(shè)指南》、《高性能網(wǎng)站建設(shè)進(jìn)階指南》。經(jīng)驗(yàn)豐富的工程師對于前端性能優(yōu)化方法耳濡目染,基本都能一一列舉出來。這些性能優(yōu)化原則大概是在7年前提出的,對于web性能優(yōu)化至今都有非常重要的指導(dǎo)意義。
然而,對于構(gòu)建大型web應(yīng)用的團(tuán)隊(duì)來說,要堅(jiān)持貫徹這些優(yōu)化原則并不是一件十分容易的事。因?yàn)閮?yōu)化原則中很多要求是與工程管理相違背的,比如?把css放在頭部
?和?把js放在尾部
?這兩條原則,我們不能讓團(tuán)隊(duì)的工程師在寫樣式和腳本引用的時候都去修改一個相同的頁面文件。這樣做會嚴(yán)重影響團(tuán)隊(duì)成員間并行開發(fā)的效率,尤其是在團(tuán)隊(duì)有版本管理的情況下,每天要花大量的時間進(jìn)行代碼修改合并,這項(xiàng)成本是難以接受的。因此在前端工程界,總會看到周期性的性能優(yōu)化工作,辛勤的前端工程師們每到月圓之夜就會傾巢出動根據(jù)優(yōu)化原則做一次性能優(yōu)化。
性能優(yōu)化是一個工程問題
本文將從一個全新的視角來思考web性能優(yōu)化與前端工程之間的關(guān)系,揭示前端性能優(yōu)化在前端架構(gòu)及開發(fā)工具設(shè)計(jì)層面的實(shí)現(xiàn)思路。
po主先假設(shè)本文的讀者是有前端開發(fā)經(jīng)驗(yàn)的工程師,并對企業(yè)級web應(yīng)用開發(fā)及性能優(yōu)化有一定的思考,因此我不會重復(fù)介紹雅虎14條性能優(yōu)化原則。如果您沒有這些前續(xù)知識,請移步?這里?來學(xué)習(xí)。
首先,我們把雅虎14條優(yōu)化原則,《高性能網(wǎng)站建設(shè)指南》以及《高性能網(wǎng)站建設(shè)進(jìn)階指南》中提到的優(yōu)化點(diǎn)做一次梳理,按照優(yōu)化方向分類,可以得到這樣一張表格:
優(yōu)化方向 | 優(yōu)化手段 |
---|---|
請求數(shù)量 | 合并腳本和樣式表,CSS Sprites,拆分初始化負(fù)載,劃分主域 |
請求帶寬 | 開啟GZip,精簡JavaScript,移除重復(fù)腳本,圖像優(yōu)化 |
緩存利用 | 使用CDN,使用外部JavaScript和CSS,添加Expires頭, |
減少DNS查找,配置ETag,使AjaX可緩存 | |
頁面結(jié)構(gòu) | 將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出 |
代碼校驗(yàn) | 避免CSS表達(dá)式,避免重定向 |
目前大多數(shù)前端團(tuán)隊(duì)可以利用?yui compressor?或者?google closure compiler?等壓縮工具很容易做到?精簡Javascript
?這條原則;同樣的,也可以使用圖片壓縮工具對圖像進(jìn)行壓縮,實(shí)現(xiàn)?圖像優(yōu)化
?原則。這兩條原則是對單個資源的處理,因此不會引起任何工程方面的問題。很多團(tuán)隊(duì)也通過引入代碼校驗(yàn)流程來確保實(shí)現(xiàn)避免css表達(dá)式
?和?避免重定向
?原則。目前絕大多數(shù)互聯(lián)網(wǎng)公司也已經(jīng)開啟了服務(wù)端的Gzip壓縮,并使用CDN實(shí)現(xiàn)靜態(tài)資源的緩存和快速訪問;一些技術(shù)實(shí)力雄厚的前端團(tuán)隊(duì)甚至研發(fā)出了自動CSS Sprites工具,解決了CSS Sprites在工程維護(hù)方面的難題。使用“查找-替換”思路,我們似乎也可以很好的實(shí)現(xiàn)?劃分主域
?原則。
我們把以上這些已經(jīng)成熟應(yīng)用到實(shí)際生產(chǎn)中的優(yōu)化手段去除掉,留下那些還沒有很好實(shí)現(xiàn)的優(yōu)化原則。再來回顧一下之前的性能優(yōu)化分類:
優(yōu)化方向 | 優(yōu)化手段 |
---|---|
請求數(shù)量 | 合并腳本和樣式表,拆分初始化負(fù)載 |
請求帶寬 | 移除重復(fù)腳本 |
緩存利用 | 添加Expires頭,配置ETag,使Ajax可緩存 |
頁面結(jié)構(gòu) | 將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出 |
有很多頂尖的前端團(tuán)隊(duì)可以將上述還剩下的優(yōu)化原則也都一一解決,但業(yè)界大多數(shù)團(tuán)隊(duì)都還沒能很好的解決這些問題。因此,本文將就這些原則的解決方案做進(jìn)一步的分析與講解,從而為那些還沒有進(jìn)入前端工業(yè)化開發(fā)的團(tuán)隊(duì)提供一些基礎(chǔ)技術(shù)建設(shè)意見,也借此機(jī)會與業(yè)界頂尖的前端團(tuán)隊(duì)在工業(yè)化工程化方向上交流一下彼此的心得。
緩存利用
?分類中保留了?添加Expires頭
?和?配置ETag
?兩項(xiàng)?;蛟S有些人會質(zhì)疑,明明這兩項(xiàng)只要配置了服務(wù)器的相關(guān)選項(xiàng)就可以實(shí)現(xiàn),為什么說它們難以解決呢?確實(shí),開啟這兩項(xiàng)很容易,但開啟了緩存后,我們的項(xiàng)目就開始面臨另一個挑戰(zhàn):?如何更新這些緩存?
相信大多數(shù)團(tuán)隊(duì)也找到了類似的答案,它和《高性能網(wǎng)站建設(shè)指南》關(guān)于“添加Expires頭”所說的原則一樣——修訂文件名。即:
最有效的解決方案是修改其所有鏈接,這樣,全新的請求將從原始服務(wù)器下載最新的內(nèi)容。
思路沒錯,但要怎么改變鏈接呢?變成什么樣的鏈接才能有效更新緩存,又能最大限度避免那些沒有修改過的文件緩存不失效呢?
先來看看現(xiàn)在一般前端團(tuán)隊(duì)的做法:
<h1>hello world</h1>
<script type="text/javascript" src="a.js?t=201404231123"></script>
<script type="text/javascript" src="b.js?t=201404231123"></script>
<script type="text/javascript" src="c.js?t=201404231123"></script>
<script type="text/javascript" src="d.js?t=201404231123"></script>
<script type="text/javascript" src="e.js?t=201404231123"></script>
ps: 也有團(tuán)隊(duì)采用構(gòu)建版本號為靜態(tài)資源請求添加query,它們在本質(zhì)上是沒有區(qū)別的,在此就不贅述了。
接下來,項(xiàng)目升級,比如頁面上的html結(jié)構(gòu)發(fā)生變化,對應(yīng)還要修改?a.js
?這個文件,得到的構(gòu)建結(jié)果如下:
<header>hello world</header>
<script type="text/javascript" src="a.js?t=201404231826"></script>
<script type="text/javascript" src="b.js?t=201404231826"></script>
<script type="text/javascript" src="c.js?t=201404231826"></script>
<script type="text/javascript" src="d.js?t=201404231826"></script>
<script type="text/javascript" src="https://atts.w3cschool.cn/attachments/image/cimg/pre>
為了觸發(fā)用戶瀏覽器的緩存更新,我們需要更改靜態(tài)資源的url地址,如果采用構(gòu)建信息(時間戳、版本號等)作為url修改的依據(jù),如上述代碼所示,我們只修改了一個a.js文件,但再次構(gòu)建會讓所有請求都更改了url地址,用戶再度訪問頁面那些沒有修改過的靜態(tài)資源的(b.js,b.js,c.js,d.js,e.js)的瀏覽器緩存也一同失效了。
使用構(gòu)建信息作為靜態(tài)資源更新標(biāo)記會導(dǎo)致每次構(gòu)建發(fā)布后所有靜態(tài)資源都被迫更新,瀏覽器緩存利用率降低,給性能帶來傷害。
此外,采用添加query的方式來清除緩存還有一個弊端,就是?覆蓋式發(fā)布
?的上線問題。
更多建議: