前端效能優化常用總結

zimo發表於2017-10-14

前言

對於前端的效能話題,從來都沒有斷絕過。因為這個東西沒有最好,只有更好。而且往往也是業務的繁雜程度去決定優化程度的。作為一個前端開發者,效能是我們關注的指標。它直接影響著我們的使用者,同時也影響著產品本身。前端發展以來,優化方式,琳琅滿目,有雅虎軍規等。這些內容複雜繁多,往往容易被人遺忘。因此,本篇對於這些常用的優化方式進行總結,或許,並不全面,見諒。如果你喜歡我的文章,歡迎評論,歡迎Star~。歡迎關注我的github部落格

正文

前端優化層出不窮,移動端大行其道的現在,我們可以說優化好移動端,PC端也將會更好。所以,我們可以綜合以下圖片進行一些分析,如圖:

優化
優化

圖中已經對前端效能做了一些概括。但其實,我覺得我們可以將這個概括更加精準,扼要,豐富。所以,接下來我會從三個方面就前端效能進行總結:網路方面、DOM操作及渲染方面、資料方面。

網路方面

web應用,總是會有一部分的時間浪費在網路連線和資源下載方面。往往建立一次網路連線是需要時間成本的。而且瀏覽器同一時間所傳送的網路請求數是有限的。所以,這個層面的優化可以從「減少請求數目」開始:

  1. 減少http請求:在YUI35規則中也有提到,主要是優化js、css和圖片資源三個方面,因為html是沒有辦法避免的。因此,我們可以做一下的幾項操作:

    • 合併js檔案
    • 合併css檔案
    • 雪碧圖的使用(css sprite)
    • 使用base64表示簡單的圖片

      上述四個方法,前面兩者我們可以使用webpack之類的打包工具進行打包;雪碧圖的話,也有專門的製作工具;圖片的編碼是使用base64的,所以,對於一些簡單的圖片,例如空白圖等,可以使用base64直接寫入html中。

回到之前網路層面的問題,除了減少請求數量來加快網路載入速度,往往整個資源的體積也是,平時我們會關注的方面。

  1. 減小資源體積:可以通過以下幾個方面進行實施:

    • gzip壓縮
    • js混淆
    • css壓縮
    • 圖片壓縮

      gzip壓縮主要是針對html檔案來說的,它可以將html中重複的部分進行一個打包,多次複用的過程。js的混淆可以有簡單的壓縮(將空白字元刪除)、醜化(醜化的方法,就是將一些變數縮小)、或者可以使用php對js進行混淆加密。css壓縮,就是進行簡單的壓縮。圖片的壓縮,主要也是減小體積,在不影響觀感的前提下,儘量壓縮圖片,使用png等圖片格式,減少向量圖、高清圖等的使用。這樣子的做法不僅可以加快網頁顯示,也能減少流量的損耗。

除了以上兩部分的操作之外,在網路層面我們還需要做好快取工作。真正的效能優化來說,快取是效率最高的一種,往往縮短的載入時間也是最大的。

  1. 快取:可以通過以下幾個方面來描述:

    • DNS快取
    • CDN部署與快取
    • http快取

      由於瀏覽器會在DNS解析步驟中消耗一定的時間,所以,對於一些高訪問量網站來說,做好DNS的快取工作,就會一定程度上提升網站效率。CDN快取,CDN作為靜態資原始檔的分發網路,本身就已經提升了,網站靜態資源的獲取速度,加快網站的載入速度,同時也給靜態資源做好快取工作,有效的利用已快取的靜態資源,加快獲取速度。http快取,也是給資源設定快取時間,防止在有效的快取時間內對資源進行重複的下載,從而提升整體網頁的載入速度。

其實,網路層面的優化還有很多,特別是針對於移動端頁面來說。眾所周知,移動端對於網路的敏感度更加的高,除了目前的4G和WIFI之外,其他的移動端網路相當於弱網環境,在這種環境下,資源的快取利用是相當重要的。而且,減少http的請求次數,也是至關重要的,移動端弱網環境下,對於http請求的時間也會增加。所以,我們可以看一下我們在移動端網路方面可以做的優化:

  1. 移動端優化:使用以下幾種方式來加快移動端網路方面的優化:

    • 使用長cache,減少重定向
    • 首屏優化,保證首屏載入資料小於14kb
    • 不濫用web字型

      「使用長cache」,可以使得移動端的部分資源設定長期快取,這樣可以保證資源不用向伺服器傳送請求,來比較資源是否更新,從而避免304的情況。304重定向,在PC端或許並不會影響網頁的載入速度,但是,在移動端網路不穩定的前提下,多一次請求,就多了一部分載入時間。「首屏優化」,對於移動端來說是至關重要的。2s時間是使用者的最佳體驗,一旦超出這個時間,將會導致使用者的流失。所以,針對移動端的網路情況,不可能在這麼短時間內載入完成所有的網頁資源,所以我們必須保證首屏中的內容被優先顯示出來,而且基於TCP的慢啟動和擁塞控制,第一個14kb的資料是非常重要的,所以需要保證首部載入資料能夠小於14kb。「不濫用web字型」,web字型的好處就是,可以代替某些圖片資源,但是,在移動端過多的web字型的使用,會導致頁面資源載入的繁重,所以,慎用web字型

渲染和DOM操作方面

首先,簡單的聊一下優化渲染的重要性。在網頁初步載入時,獲取到HTML檔案之後,最初的工作是構建DOM和構建CSSOM兩個樹,之後將他們合併形成渲染樹,最後對其進行列印。我們可以通過圖片來看一下,簡單的過程:

DOM渲染
DOM渲染

這裡整個過程拉出來寫,具體可以再寫一篇文章,恕我偷下懶,推薦一篇比較好的文章給大家吧。瀏覽器渲染過程與效能優化

繼續我們的話題,我們可以如何去縮短這個過程呢?可以從以下幾個操作進行優化。

  1. 優化網頁渲染

    • css的檔案放在頭部,js檔案放在尾部或者非同步
    • 儘量避免內聯樣式

      css檔案放在「頭部載入」,可以保證解析DOM的同時,解析css檔案。因為,CSS(外鏈或內聯)會阻塞整個DOM的渲染,然而DOM解析會正常進行,所以將css檔案放在頭部進行解析,可以加快網頁的構建速度。假設將其放在尾部,那時DOM樹幾乎構建,這時就得等到CSSOM樹構建完成,才能夠繼續下面的步驟。「js放在尾部」:js檔案不同,將js檔案放在尾部或者非同步載入的原因是JS(外鏈或內聯)會阻塞後續DOM的解析,後續DOM的渲染也將被阻塞,而且一旦js中遇到DOM元素的操作,很可能會影響。這方面可以推薦一篇文章——非同步指令碼載入提高頁面效能。「避免使用內聯樣式」,可以有效的減少html的體積,一般考慮內聯樣式的時候,往往是樣式本身體積比較小,往往載入網路資源的時間會大於它的時候。

除了頁面渲染層面的優化,當然最重要的就是DOM操作方面的優化,這部分的優化應該是最多的,而且也是平時開發可以注意的地方。如果開發前期明白這些原理,同時付諸實踐的話,就可以在後期的效能完善上面少下很多功夫。那麼,接下來我們可以來看一下具體的操作:

  1. DOM操作優化

    • 避免在document上直接進行頻繁的DOM操作
    • 使用classname代替大量的內聯樣式修改
    • 對於複雜的UI元素,設定position為absolute或fixed
    • 儘量使用css動畫
    • 使用requestAnimationFrame代替setInterval操作
    • 適當使用canvas
    • 儘量減少css表示式的使用
    • 使用事件代理

      前面三個操作,其實都是希望『減少迴流和重繪』。其實,進行一次DOM操作的代價是非常之大的,以前可以通過網頁操作是否卡頓來進行判斷,但是,現代瀏覽器的進步已經大大減少了這方面的影響。但是,我們還是需要清楚,如何去減少迴流和重繪的問題。因為這裡不想細說這方面的知識,想要了解的話,可以看這篇文章——迴流與重繪:CSS效能讓JavaScript變慢?。這可是張鑫旭大大的一篇文章呦(^.^)。「儘量使用css動畫」,是因為本身css動畫比較簡單,而且相較於js的複雜動畫,瀏覽器本身對其進行了優化,使用上面不會出現卡頓等問題。「使用requestAnimationFrame代替setInterval操作」,相信大家都有所耳聞,setInterval定時器會有一定的延時,對於變動性高的動畫來說,會出現卡頓現象。而requestAnimationFrame正好解決的整個問題。「適當使用canvas」,不得不說canvas是前端的一個進步,出現了它之後,前端介面的複雜性也隨之提升了。一些難以完成的動畫,都可以使用canvas進行輔助完成。但是,canvas使用頻繁的話,會加重瀏覽器渲染的壓力,同時導致效能的下降。所以,適當時候使用canvas是一個不錯的建議。「儘量減少css表示式的使用」,這個在YUI規則中也被提到過,往往css的表示式在設計之初都是美好的,但在使用過程中,由於其頻繁觸發的特性,會拖累網頁的效能,出現卡頓。因此在使用過程中儘量減少css表示式的使用,可以改換成js進行操作。「使用事件代理」:往往對於具備冒泡性質的事件來說,使用事件代理不失為一種好的方法。舉個例子:一段列表都需要設定點選事件,這時如果你給列表中的每一項設定監聽,往往會導致整體的效能下降,但是如果你給整個列表設定一個事件,然後通過點選定位目標來觸發相應的操作,往往效能就會得到改善。

DOM操作的優化,還有很多,當然也包括移動端的。這個會在之後移動端優化部分被提及,此處先賣個關子。上面我們概述了開始渲染的時候和DOM操作的時候的一些注意事項。接下來要講的是一些小細節的注意,這些細節可能對於頁面影響不大,但是一旦堆積多了,效能也會有所影響。

  1. 操作細節注意

    • 避免圖片或者frame使用空src
    • 在css屬性為0時,去掉單位
    • 禁止影象縮放
    • 正確的css字首的使用
    • 移除空的css規則
    • 對於css中可繼承的屬性,如font-size,儘量使用繼承,少一點設定
    • 縮短css選擇器,多使用偽元素等幫助定位

      上述的一些操作細節,是平時在開發中被要求的,更可以理解為開發規範。(基本操作,坐下^_^)

列舉完基本操作之後,我們再來聊一下移動端在DOM操作方面的一些優化。

  1. 移動端優化

    • 長列表滾動優化
    • 函式防抖和函式節流
    • 使用touchstart、touchend代替click
    • HTML的viewport設定
    • 開啟GPU渲染加速

      首先,長列表滾動問題,是移動端需要面對的,IOS儘量使用區域性滾動,android儘量使用全域性滾動。同時,需要給body新增上-webkit-overflow-scrolling: touch來優化移動段的滾動。如果有興趣的同學,可以去了解一下ios和android滾動操作上的區別以及優化。「防抖和節流」,設計到滾動等會被頻繁觸發的DOM事件,需要做好防抖和節流的工作。它們都是為了限制函式的執行頻次,以優化函式觸發頻率過高導致的響應速度跟不上觸發頻率,出現延遲,假死或卡頓的現象。

      介紹:函式防抖,當呼叫動作過n毫秒後,才會執行該動作,若在這n毫秒內又呼叫此動作則將重新計算執行時間;函式節流,預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新週期。

      「touchstart、touchend代替click」,也是移動端比較常用的操作。click在移動端會有300ms延時,這應該是一個常識唄。(不知道的小夥伴該收藏一下呦)。這種方法會影響使用者的體驗。所以做優化時,最簡單的方法就是使用touchstart或者touchend代替click。因為它們事件執行順序是touchstart->touchmove->touchend->click。或者,使用fastclick或者zepto的tap事件代替click事件。「HTML的viewport設定」,可以防止頁面的縮放,來優化效能。「開啟GPU渲染加速」,小夥伴們一定聽過CPU吧,但是這裡的GPU不能和CPU混為一談呦。GPU的全名是Graphics Processing Unit,是一種硬體加速方式。一般的css渲染,瀏覽器的渲染引擎都不會使用到它。但是,在3D渲染時,計算量較大,繁重,瀏覽器會開啟顯示卡的硬體加速來幫助完成這些操作。所以,我們這裡可以使用css中的translateZ設定,來欺騙瀏覽器,讓其幫忙開啟GPU加速,加快渲染程式。

DOM部分的優化,更多的是習慣。需要自己強制要求自己在開發過程中去注意這些規範。所以,這部分的內容可以多關注一下,才能夠慢慢了解。同時,本人對於上述幾點的描述是概括性的。並沒有對其進行詳細的展開。因此,也要求你去細細的查閱Google呦。

資料方面

資料,也可以說是前端優化方面比較重要的一塊內容。頁面與使用者的互動響應,往往伴隨著資料互動,處理,以及ajax的非同步請求等內容。所以,我們也可以來聊聊這一塊的知識。首先是對於圖片資料的處理:

  1. 圖片載入處理

    • 圖片預載入
    • 圖片懶載入
    • 首屏載入時進度條的顯示

      「圖片預載入」,預載入的寓意就是提前載入內容。而圖片的預載入往往會被用在圖片資源比較大,即時載入時會導致很長的等待過程時,才會被使用的。常見場景:圖片漫畫展示時。往往會預載入一張到兩張的圖片。「圖片懶載入」,懶載入或許你是第一次聽說,但是,這種方式在開發中會被經常使用。首先,我們需要明白一個道理:往往只有看到的資源是必須的,其他資源是可以隨著使用者的滾動,隨即顯示的。所以,特別是對於圖片資源特別多的網站來說,做好圖片的懶載入是可以大大提升網頁的載入速度的。

      常見的圖片懶載入的方式就是:在最初給圖片的src設定一個比較簡單的圖片,然後將圖片的真實地址設定給自定義的屬性,做一個佔位,然後給圖片設定監聽事件,一旦圖片到達視口範圍,從圖片的自定義屬性中獲取出真是地址,然後賦值給src,讓其進行載入。

      「首屏進度條的顯示」:往往對於首屏優化後的資料量並不滿意的話,同時也不能進一步縮短首屏包的長度了,就可以使用進度條的方式,來提醒使用者進行等待。

講完了圖片這一塊資料資源的處理,往往我們需要去優化一下非同步請求這一部分的內容。因為,非同步的資料獲取也是前端不可分割的。這一部分我們也可以做一定的處理:

  1. 非同步請求的優化

    • 使用正常的json資料格式進行互動
    • 部分常用資料的快取
    • 資料埋點和統計

      「JSON互動」,JSON的資料格式輕巧,結構簡單,往往可以大大優化前後端的資料通訊。「常用資料的快取」,可以將一些使用者的基本資訊等常用的資訊做一個快取,這樣可以保證ajax請求的減少。同時,HTML5新增的storage的內容,也不用怕cookie暴露,引起的資訊洩漏問題。「資料埋點和統計」,對於資深的程式設計師來說,比較瞭解。而且目前的大部分公司也會做這方面的處理。有心的小夥伴可以自行查閱。

最後,還有就是大量資料的運算。對於javascript語言來說,本身的單執行緒就限制了它並不能計算大量的資料,往往會造成頁面的卡頓。而可能業務中有些複雜的UI需要去執行大量的運算,所以,webWorker的使用是至關重要的。或許,前端標準普及的落後,會導致大家對於這些新生事物的短暫缺失吧。

總結

本篇文章就前端效能這個話題做了一個總結。或許,並不全面,但是都是一些平時開發中會被經常用到的知識。希望有心者能夠去親身的嘗試一下這些方面的優化。本篇的概述了一下幾個知識點:

  • 網路層面的優化
  • 資料層面的優化
  • DOM操作與渲染層面的優化

如果你對我寫的有疑問,可以評論,如我寫的有錯誤,歡迎指正。你喜歡我的部落格,請給我關注Star~呦。大家一起總結一起進步。歡迎關注我的github部落格

最後打個廣告

聽說寫文章可以得到非同步社群的書,非同步社群作為國內頂尖的IT專業圖書社群,它的書我非常想要,所以我竭力寫了這篇文章,我想要這本書,大家如果覺得好給我點個贊吧^-^

相關文章