前端工程與性能優(yōu)化

2018-02-24 15:49 更新

原文出處:https://github.com/fouber/blog/issues/3
作者:fouber

目錄

每個參與過開發(fā)企業(yè)級web應用的前端工程師或許都曾思考過前端性能優(yōu)化方面的問題。我們有雅虎14條性能優(yōu)化原則,還有兩本很經(jīng)典的性能優(yōu)化指導書:《高性能網(wǎng)站建設指南》、《高性能網(wǎng)站建設進階指南》。經(jīng)驗豐富的工程師對于前端性能優(yōu)化方法耳濡目染,基本都能一一列舉出來。這些性能優(yōu)化原則大概是在7年前提出的,對于web性能優(yōu)化至今都有非常重要的指導意義。

高性能網(wǎng)站建設指南

然而,對于構(gòu)建大型web應用的團隊來說,要堅持貫徹這些優(yōu)化原則并不是一件十分容易的事。因為優(yōu)化原則中很多要求是與工程管理相違背的,比如?把css放在頭部?和?把js放在尾部?這兩條原則,我們不能讓團隊的工程師在寫樣式和腳本引用的時候都去修改一個相同的頁面文件。這樣做會嚴重影響團隊成員間并行開發(fā)的效率,尤其是在團隊有版本管理的情況下,每天要花大量的時間進行代碼修改合并,這項成本是難以接受的。因此在前端工程界,總會看到周期性的性能優(yōu)化工作,辛勤的前端工程師們每到月圓之夜就會傾巢出動根據(jù)優(yōu)化原則做一次性能優(yōu)化。

性能優(yōu)化是一個工程問題

本文將從一個全新的視角來思考web性能優(yōu)化與前端工程之間的關系,揭示前端性能優(yōu)化在前端架構(gòu)及開發(fā)工具設計層面的實現(xiàn)思路。

性能優(yōu)化原則及分類

po主先假設本文的讀者是有前端開發(fā)經(jīng)驗的工程師,并對企業(yè)級web應用開發(fā)及性能優(yōu)化有一定的思考,因此我不會重復介紹雅虎14條性能優(yōu)化原則。如果您沒有這些前續(xù)知識,請移步?這里?來學習。

首先,我們把雅虎14條優(yōu)化原則,《高性能網(wǎng)站建設指南》以及《高性能網(wǎng)站建設進階指南》中提到的優(yōu)化點做一次梳理,按照優(yōu)化方向分類,可以得到這樣一張表格:

優(yōu)化方向 優(yōu)化手段
請求數(shù)量 合并腳本和樣式表,CSS Sprites,拆分初始化負載,劃分主域
請求帶寬 開啟GZip,精簡JavaScript,移除重復腳本,圖像優(yōu)化
緩存利用 使用CDN,使用外部JavaScript和CSS,添加Expires頭,
減少DNS查找,配置ETag,使AjaX可緩存
頁面結(jié)構(gòu) 將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出
代碼校驗 避免CSS表達式,避免重定向

目前大多數(shù)前端團隊可以利用?yui compressor?或者?google closure compiler?等壓縮工具很容易做到?精簡Javascript?這條原則;同樣的,也可以使用圖片壓縮工具對圖像進行壓縮,實現(xiàn)?圖像優(yōu)化?原則。這兩條原則是對單個資源的處理,因此不會引起任何工程方面的問題。很多團隊也通過引入代碼校驗流程來確保實現(xiàn)避免css表達式?和?避免重定向?原則。目前絕大多數(shù)互聯(lián)網(wǎng)公司也已經(jīng)開啟了服務端的Gzip壓縮,并使用CDN實現(xiàn)靜態(tài)資源的緩存和快速訪問;一些技術實力雄厚的前端團隊甚至研發(fā)出了自動CSS Sprites工具,解決了CSS Sprites在工程維護方面的難題。使用“查找-替換”思路,我們似乎也可以很好的實現(xiàn)?劃分主域?原則。

我們把以上這些已經(jīng)成熟應用到實際生產(chǎn)中的優(yōu)化手段去除掉,留下那些還沒有很好實現(xiàn)的優(yōu)化原則。再來回顧一下之前的性能優(yōu)化分類:

優(yōu)化方向 優(yōu)化手段
請求數(shù)量 合并腳本和樣式表,拆分初始化負載
請求帶寬 移除重復腳本
緩存利用 添加Expires頭,配置ETag,使Ajax可緩存
頁面結(jié)構(gòu) 將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出

有很多頂尖的前端團隊可以將上述還剩下的優(yōu)化原則也都一一解決,但業(yè)界大多數(shù)團隊都還沒能很好的解決這些問題。因此,本文將就這些原則的解決方案做進一步的分析與講解,從而為那些還沒有進入前端工業(yè)化開發(fā)的團隊提供一些基礎技術建設意見,也借此機會與業(yè)界頂尖的前端團隊在工業(yè)化工程化方向上交流一下彼此的心得。

靜態(tài)資源版本更新與緩存

緩存利用?分類中保留了?添加Expires頭?和?配置ETag?兩項?;蛟S有些人會質(zhì)疑,明明這兩項只要配置了服務器的相關選項就可以實現(xiàn),為什么說它們難以解決呢?確實,開啟這兩項很容易,但開啟了緩存后,我們的項目就開始面臨另一個挑戰(zhàn):?如何更新這些緩存?

相信大多數(shù)團隊也找到了類似的答案,它和《高性能網(wǎng)站建設指南》關于“添加Expires頭”所說的原則一樣——修訂文件名。即:

最有效的解決方案是修改其所有鏈接,這樣,全新的請求將從原始服務器下載最新的內(nèi)容。

思路沒錯,但要怎么改變鏈接呢?變成什么樣的鏈接才能有效更新緩存,又能最大限度避免那些沒有修改過的文件緩存不失效呢?

先來看看現(xiàn)在一般前端團隊的做法:

<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: 也有團隊采用構(gòu)建版本號為靜態(tài)資源請求添加query,它們在本質(zhì)上是沒有區(qū)別的,在此就不贅述了。

接下來,項目升級,比如頁面上的html結(jié)構(gòu)發(fā)生變化,對應還要修改?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)資源更新標記會導致每次構(gòu)建發(fā)布后所有靜態(tài)資源都被迫更新,瀏覽器緩存利用率降低,給性能帶來傷害。

此外,采用添加query的方式來清除緩存還有一個弊端,就是?覆蓋式發(fā)布?的上線問題。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號