從案例分析如何優化前端效能
在 De Voorhoede工作的日子裡,我們一直在追尋為使用者構建高效能的前端解決方案。不過並不是每個客戶會樂於遵循我們的效能指南,以至於我們必須一遍又一遍地跟他們解釋那些保證他們能夠戰勝競爭對手的效能策略的重要性。最近我們也重構了自己的官方主頁,使其能夠擁有更快地響應速度與更好地效能表現。
效能調優始於設計
在前端專案中,我們常常與產品經理以及UI設計討論如何在美感與效能之間達到平衡,我們堅信更快地內容呈現是好的使用者體驗的不可分割的一部分。在我們自己的網站中,我們是以效能優於美感。好的內容、佈局、圖片與互動都是構成你網站吸引力的不可或缺的部分,不過這些複雜的元素的使用往往也意味著頁面載入速度的增加。設計的核心即在於決定我們網站需要呈現哪些內容,往往這裡的內容會指圖片、字型這樣的偏靜態的部分,我們首先也從對於靜態內容的優化開始。
Static Site Generator
為了演示與測試方便,我們基於NodeJS搭建了一個混合使用MarkDown與JSON作為配置的靜態網站生成器,其中一個簡單的部落格型別的網站的配置資訊如下:
JavaScript12345 | {"keywords":["performance","critical rendering path","static site","..."],"publishDate":"2016-08-12","authors":["Declan"]} |
而其內容為:
JavaScript1234 | #Acasestudy on boosting front-endperformanceAt[De Voorhoede](https://www.voorhoede.nl/en/) we try to boost front-end performance...##Design forperformanceInour projects we have daily discussions... |
下面,我們就這個靜態網站,進行一些討論。
Image Delivery
圖片是網站的不可或缺的部分,其能夠大大提升網站的表現力與視覺效果,而目前平均大小為2406KB的網頁中就有1535KB是圖片資源,可見圖片佔據了靜態資源多麼大的一個比重,這也是我們需要重點優化的部分。
WebP
WebP 是面向現代網頁的高壓縮低損失的圖片格式,通常會比JPEG小25%左右。然後WebP目前被很多人忽視,也不常使用。截止到本文撰寫的時候,WebP目前只能夠在Chrome, Opera and Android (大概佔用戶數的 50%)這些瀏覽器中使用,不過我們還是有辦法以JPG/PNG來彌補部分瀏覽器中不支援WebP的缺憾。
picture
標籤
使用picture標籤可以方便的對於WebP格式不支援的情況下完成替換:
XHTML123456789 | <picture><source type="image/webp"srcset="image-l.webp"media="(min-width: 640px)"><source type="image/webp"srcset="image-m.webp"media="(min-width: 320px)"><source type="image/webp"srcset="image-s.webp"><source srcset="image-l.jpg"media="(min-width: 640px)"><source srcset="image-m.jpg"media="(min-width: 320px)"><source srcset="image-s.jpg"><img alt="Description of the image"src="image-l.jpg"></picture> |
這裡我們使用了 picturefill by Scott Jehl作為Polyfill庫來保證低版本的瀏覽器中能夠支援picture標籤,並且保證跨瀏覽器的功能一致性。並且我們還使用了img標籤來保證那些不支援picture的瀏覽器能夠正常工作。
圖片多格式生成
現在我們已經可以通過設定不同的圖片尺寸、格式來保證圖片的分發優化,不過我們總不希望每次要用一張圖片的時候就去生成6個不同的尺寸/例項。我們希望有一種抽象的方法可以幫我們自動完成這一步,為我們自動生成不同的格式/尺寸,然後自動插入合適的picture元素,在我們的靜態網站生成器中是這麼做的:
- 首先是要gulp responsive來生成不同尺寸的圖片,該外掛同樣會輸出WebP格式的圖片
- 壓縮生成好的圖片
- 使用者只需要在MarkDown中編寫
![Description of the image](image.jpg)
即可 - 我們自定義的MarkDown渲染引擎會在處理過程中自動使用picture元素替換這些img標籤
SVG Animation
我們的網站中也存在著很多的Icon以及動畫性質圖片,這裡我們是選擇SVG作為Icon與Animation的格式,主要考慮有下:
- SVG是矢量表示,往往比點陣圖檔案更小
- SVG自帶響應式功效,能夠根據容器大小進行自動縮放,因此我們不需要再為了picture元素生成不同尺寸的圖片
- 最重要的一點是我們可以使用CSS去改變其樣式或者新增動畫效果,關於這一點可以參考CodePen上的這個演示。
Custom Web Fonts
我們首先回顧下瀏覽器是如何使用自定義字型的,當瀏覽器識別到使用者在CSS中基於@font-size
定義的字型時,會嘗試下載該字型檔案。而在下載的過程中,瀏覽器是不會展示該字型所屬的文字內容,最終導致了所謂的Flash of Invisible Text
現象。現在很多的網站都存在這個問題,這也是導致使用者體驗差的一個重要原因,即會影響使用者最主要的內容瀏覽這一操作。而我們的優化點即在於首先將字型設定為預設字型,而後在自定義的Web Font下載完畢之後對標準字型再進行替換操作,並且重新渲染整個文字塊。而如果自定義的字型下載失敗,整個內容還是能保證基本的可讀性,不會對使用者體驗造成毀滅性的打擊。
首先,我們會為需要使用到的Web Fonts建立最小子集,即只將那些需要使用的字型提取出來,而並不需要讓使用者下載整個字型集,這裡推薦使用Font squirrel webfont generator。另外,我們還需要為字型的下載設定監視器,即保證能夠在字型下載完畢之後自動回撥,這裡我們使用的是fontfaceobserver,它會為頁面自動建立一個監視器,在偵測到所有的自定義Web Fonts下載完畢後,會為整個頁面新增預設的類名:
CSS12 | html {font-family:Georgia,serif;}html.fonts-loaded {font-family:Noto,Georgia,serif;} |
不過現在CSS的font-display
屬性也原生提供了我們這種替換功能,更多詳情可見font-display屬性。
JS 與 CSS 的懶載入
總的來說我們希望所有的資源能夠儘可能快地載入完畢,不過往往為了保證首頁載入的速度,我們會考慮將部分非首屏需要的JS/CSS檔案進行延遲載入,或者對於重複的檢視使用瀏覽器本地快取。
Lazy Load JS
目前來說,我們的網站都是偏向於靜態,並不需要太多的JavaScript介入,不過考慮到日後的擴充套件空間,我們還是構建了一套完整的JS的工作流。眾所周知,如果將JS直接放置到head標籤中,其會阻塞整個頁面的渲染。對於該點,最簡單的方式就是將會阻塞渲染的JS指令碼移動到頁面的尾部,在整個首屏渲染完畢之後再進行載入。另一個常用的手段就是依然保持JS檔案位於head標籤中,不過為其新增一個defer
的屬性,這保證了瀏覽器只會先將該指令碼下載下來,然後等到整個頁面載入完畢再執行該指令碼。
另一個需要注意的是,因為我們並不使用類似於jQuery這樣的第三方依賴庫,而更多的依賴於瀏覽器原生的特性,因此我們希望在合適的瀏覽器內載入合適版本的JS程式碼,其效果大概如下:
123456 | <script>// Mustard Cuttingif('querySelector'indocument&&'addEventListener'inwindow){document.write('<script src="index.js" defer><\/script>');}</script> |
Lazy Load CSS
正如上文所述,我們的網站偏向於靜態展示,因此首屏的最大問題就是CSS檔案的載入問題。瀏覽器會在head標籤中宣告的所有CSS檔案下載完畢之前一直處於阻塞狀態,這種機制很是明智的,不然的話瀏覽器在載入多個CSS檔案的時候會進行重複的佈局與渲染,這更是對於效能的浪費。
為了避免非首屏的CSS檔案阻塞頁面渲染,我們使用loadCSS這個小的工具庫來進行非同步的CSS檔案載入,它會在CSS檔案載入完畢後執行回撥。不過,非同步載入CSS也會帶來一個新的問題,如果我們將所有的CSS全部設定為了非同步載入,那麼使用者會首先看到單純的HTML頁面,這也會給使用者不好的體驗。那麼我們就需要在非同步載入與首屏渲染之間找到一個平衡點,即首先載入那些必要的CSS檔案。
我們一般將首屏渲染中必要的CSS檔案成為Critical CSS,即關鍵的CSS檔案,代指在保證頁面的可讀性的前提下需要載入的最少的CSS檔案數目。Critical CSS的選定會是一個非常耗時的過程,特別是我們網站本身的CSS樣式設定也在不停變更,我們不可能完全依賴於人工去提取出關鍵的CSS檔案,這裡推薦Critical這個輔助工具能夠幫你自動提取壓縮Critical CSS。下圖的一個對比即是僅載入Critical CSS與載入全部CSS的區別:
上圖中紅色的線,即是所謂的摺疊分割點。
服務端與快取
高效能的前端離不開服務端的支援,在我們的實踐中也發現不同的服務端配置同樣會影響到前端的效能。目前我們主要使用Apache Web Server作為中介軟體,並且通過HTTPS來安全地傳遞內容。
Configuration
我們首先對於合適的服務端配置做了些調研,這裡推薦是使用H5BP Boilerplate Apache Configuration作為配置模板,它是個不錯的兼顧了效能與安全性的配置建議。同樣地它也提供了面向其他服務端環境的配置。我們對於大部分的HTML、CSS以及JavaScript都開啟了GZip壓縮選項,並且對於大部分的資源都設定了快取策略,詳見下文的File Level Caching章節。
HTTPS
使用HTTPS可以保證站點的安全性,但是也會影響到你網站的效能表現,效能損耗主要發生在建立SSL握手協議的時候,這會導致很多的延遲,不過我們同樣可以通過某些設定來進行優化。
- 設定HTTP Strict Transport Security請求頭可以讓服務端告訴瀏覽器其只允許通過HTTPS進行互動,這就避免了瀏覽器從HTTP再重定向到HTTPS的時間消耗。
- 設定TLS false start允許客戶端在第一輪TLS中就能夠立刻傳遞加密資料。握手協議餘下的操作,譬如確認沒有人進行中間人監聽可以同步進行,這一點也能節約部分時間。
- 設定TLS Session Resumption,當瀏覽器與服務端曾經通過TLS進行過通訊,那麼瀏覽器會自動記錄下Session Identifier,當下次需要重新建立連線的時候,其可以複用該Identifier,從而解決了一輪的時間。
Cookies
我們並沒有使用某個服務端框架,而是直接使用了靜態的Apache Web Server,不過Apache Web Server也是能夠讀取Cookie並且進行些簡單的操作。譬如在下面這個例子中我們將CSS快取資訊存放在了Cookie中,然後交付Apache進行判斷是否需要重複載入CSS檔案:
XHTML123456789101112131415161718192021 | <!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css' )"--><noscript><link rel="stylesheet"href="0d82f.css"></noscript><script>(function(){functionloadCSS(url){...}functiononloadCSS(stylesheet,callback){...}functionsetCookie(name,value,expInDays){...}varstylesheet=loadCSS('0d82f.css');onloadCSS(stylesheet,function(){setCookie('css-loaded','0d82f',100);});}());</script><style>/* Critical CSS here */</style><!-- #else --><link rel="stylesheet"href="0d82f.css"><!-- #endif --> |
這裡Apache Server中的邏輯控制程式碼就是有點類似於註釋形式的<!-- #
,其主要包含以下步驟:
$HTTP_COOKIE!=/css-loaded/
檢測是否有設定過CSS快取相關的Cookie$HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css'
檢測快取的CSS版本是否為當前版本- If
<!-- #if expr="..." -->
值為true
我們便能假設該使用者是第一次訪問該站點 - 如果使用者是首次瀏覽,我們添加了一個
<noscript>
標籤,裡面還包含了一個阻塞型的<link rel="stylesheet">
標籤。新增該標籤的意義在於我們在下面是使用JavaScript來非同步載入CSS檔案,而在使用者禁止JavaScript的情況下也能保證可以通過該標籤來正常載入CSS檔案。 <!-- #else -->
表示式在使用者二次訪問該頁面時,我們可以認為CSS檔案已經被載入過了,因此可以直接從本地快取中載入而不需要重複請求。
上述策略同樣可以應用於Web Fonts的載入,最終的Cookie如下所示:
File Level Caching
在上文可以發現,我們嚴重依賴於瀏覽器快取來處理使用者重複訪問時資源載入的問題,理想情況下我們肯定希望能夠永久地快取CSS、JS、Fonts以及圖片檔案,然後在某個檔案發生變化的時候將快取設定為失效。這裡我們設定了以https://www.voorhoede.nl/assets/css/main.css?v=1.0.4
形式,即在請求路徑上加上版本號的方式進行快取。不過這種方式的缺陷在於如果我們更換了資原始檔的存放地址,那麼所有的快取也就自然失效了。這裡我們使用了gulp-rev以及gulp-rev-replace來為檔案新增Hash值,從而保證了僅當檔案內容發生變化的時候檔案請求路徑才會發生改變,即將每個檔案的快取驗證獨立開來。
Result
上面我們介紹了很多的優化手段,這裡我們以實驗的形式來對優化的結果與效果進行分析。我們可以用類似於PageSpeed Insights或者WebPagetest來進行效能測試或者網路分析。我覺得最好的測試你站點渲染效能的方式就是在限流的情況下觀察頁面的呈現效果,Google Chrome內建了限流的功能:
這裡我們將我們的網路環境設定為了50KB/S的GPRS網路環境,我們總共花費了2.27秒完成了首屏渲染。上圖中黃線左側的時間即指明瞭從HTML檔案開始下載到下載完成所耗費的時間,該HTML檔案中已經包含了關鍵的CSS程式碼,因此整個頁面已經保證了基本的可用性與可互動型。而剩下的比較大的資源都會進行延時載入,這正是我們想要達到的目標。我們也可以使用PageSpeed來測試下網站的效能,可以看出我們得分很不錯:
而在WebPagetest中,我們看出瞭如下的結果:
Roadmap
優化之路漫漫,永無止境,我們在未來也會關注以下幾個方面:
- HTTP/2:我們目前已經開始嘗試使用HTTP/2,而本篇文章中提到的很多的優化的要點都是面向HTTP/1.1的。簡言之,HTTP/1.1誕生之初還是處於Table佈局與行內樣式流行的時代,它並沒有考慮到現在所面對的2.6MB大小,包含200多個網路請求的頁面。為了彌合這老的協議的缺陷,我們不得不連線JS與CSS檔案、使用行內樣式、對於小圖片使用Data URL等等。這些操作都是為了節約請求次數,而HTTP/2中允許在同一個TCP請求中進行多個併發的請求,這樣就會允許我們不需要再去進行大量的檔案合併操作。
- Service Workers:這是現代瀏覽器提供的後臺工作執行緒,可以允許我們為網站新增譬如離線支援、推送訊息、後臺同步等等很多複雜的操作。
- CDN:目前我們是自己維護網站,而在真實的應用場景下可以考慮使用CDN服務來減少服務端與客戶端之間的物理距離,從而減少傳輸時延。
相關推薦
從案例分析如何優化前端效能
在 De Voorhoede工作的日子裡,我們一直在追尋為使用者構建高效能的前端解決方案。不過並不是每個客戶會樂於遵循我們的效能指南,以至於我們必須一遍又一遍地跟他們解釋那些保證他們能夠戰勝競爭對手的效能策略的重要性。最近我們也重構了自己的官方主頁,使其能夠擁有更快地響應速度與
前端效能優化 —— 前端效能分析
(點選上方公眾號,可快速關注)作者:ouvenhttps://my.oschina.net/zh
通過優化網路連線優化前端效能
【使用DNS預解析】 當瀏覽器訪問一個域名的時候,需要解析一次DNS,獲得對應域名的ip地址。在解析的過程中,按照瀏覽器快取、系統快取、路由器快取、ISP(運營商)DNS快取、根域名伺服器、頂級域名伺服器、主域名伺服器的順序,逐步讀取快取,直到拿到IP地址。 DNS Prefetch
通過減少資源優化前端效能
HTML 壓縮 HTML 程式碼壓縮就是壓縮在文字檔案中但是在HTML中不顯示的字元,包括空格、製表符、換行符等。 CSS壓縮 CSS壓縮包括無效程式碼刪除與CSS語義合併 JS壓縮與混亂 JS壓縮與混亂包括無效字元及註釋的刪除、程式碼語義化的縮減和優化、降低程式碼的可讀
146.Python修煉之路【151-前端-前端自動化及其優化-前端效能優化】2018.08.06
前端效能優化 從使用者訪問資源到資源完整的展現在使用者面前的過程中,通過技術手段和優化策略,縮短每個步驟的處理時間從而提升整個資源的訪問和呈現速度。網站的效能直接會影響到使用者的數量,所有前端效能優化很重要。 前端效能優化分為如下幾個方面: 一、程式碼部署: 1、程式碼的壓縮與合併
《從零構建前後分離的web專案》:前端終 - 徹底弄懂前端效能優化與上線 (多圖預警)
4000字長文,多圖預警!!!流量慎入!! 效能優化 - 屌絲前端效能優化、上線一條龍 大家好我又來了,本章給大家帶來的內容是:上線和上線後的效能優化 專案地址 實戰預覽地址 實戰專案地址 本章程式碼地址 本章你會了解 前端需要了解的 docker 基礎知識 部署前端專
從20秒到0.5秒:一個使用Rust語言來優化Python效能的案例
導讀:Python 被很多網際網路系統廣泛使用,但在另外一方面,它也存在一些效能問題,不過 Sentry 工程師分享的在關鍵模組上用另外一門語言 Rust 來代替 Python 的情況還是比較罕見,也在 Python 圈引發了熱議,高可用架構小編將文章翻譯轉載如下。 Sentry 是一個幫助線上業務
從Web前端優化網站效能
1. 減少HTTP請求數一個完整的請求都需要經過DNS存址、與伺服器建立連線、傳送資料、等待伺服器響應、接收資料這樣一個漫長而複雜的過程。資源上由於每個請求都要攜帶資料,因此每個請求都需要佔用頻寬。另外,由於瀏覽器進行併發請求的請求數是有上限的,因此請求數多了以後,瀏覽器需要
前端效能優化之利用 Chrome Dev Tools 進行頁面效能分析
背景 我們經常使用 Chrome Dev Tools 來開發除錯,但是很少知道怎麼利用它來分析頁面效能,這篇文章,我將詳細說明怎樣利用 Chrome Dev Tools 進行頁面效能分析及效能報告資料如何解讀。 分析面板介紹 上圖是 Chrome Dev Tools 的一個截圖,其中,我認為能用於進行頁面
從這個部落格面板邁入前端效能優化一小步
# 前置 正如你所見,我現在用的這個部落格面板,在沒優化之前幀率會降到個位數. 現在與之相比,是不是好很多呀? 下面將從滾動 scroll 優化這一方面展開,主要說一下思路. ![](https://img2020.cnblogs.com/blog/1501373/202003/1501373-20200
前端工程師必備:從瀏覽器的渲染到效能優化
摘要:本文主要講談及瀏覽器的渲染原理、流程以及相關的效能問題。 問題前瞻 1. 為什麼css需要放在頭部? 2. js為什麼要放在body後面? 3. 圖片的載入和渲染會阻塞頁面DOM構建嗎? 4. dom解析完才出現頁面嗎? 5. 首屏時間根據什麼來判定? 瀏覽器渲染 1.瀏覽器渲染圖解 [來自go
前端應該從哪些方面優化網站?
索引 ima 提高 block style 聯系 loaded article 原理 作者:斯迪鏈接:https://www.zhihu.com/question/21658448/answer/18903129來源:知乎著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業
Lighthouse前端效能優化測試工具
在前端開發中,對於自己開發的app或者web page效能的好壞,一直是讓前端開發很在意的話題。我們需要專業的網站測試工具,讓我們知道自己的網頁還有哪些需要更為優化的方面,我自己嘗試了一款工具:Lighthouse,感覺還不錯,記錄下來,也順便分享給用得著的夥伴。 Lighthouse分析web應用程式和w
2018 前端效能優化清單
下面是前端效能問題的概述,您可能需要考慮以確保您的響應時間是快速和平滑的。 2018 前端效能優化清單 - 第 1 部分 2018 前端效能優化清單 - 第 2 部分 2018 前端效能優化清單 - 第 3 部分 2018 前端效能優化清單 - 第 4 部分 &n
記錄一下前端效能優化-為何操作DOM會變慢?
對於大多數前端來說,效能優化的方法可能包括以下這些: 減少HTTP請求(合併css、js,雪碧圖/base64圖片) 壓縮(css、js、圖片皆可壓縮) 樣式表放頭部,指令碼放底部 使用CDN(這部分,不少前端都不用考慮,負責釋出的兄弟可能會負責搞好) 快取…… 不僅要避
[轉] webpack之前端效能優化(史上最全,不斷更新中。。。)
最近在用webpack優化首屏載入效能,通過幾種外掛之後我們上線前後的速度快了一倍,在此就簡單的分享下吧,先上個優化前後首屏渲染的對比圖。 可以看到總下載時間從3800ms縮短到1600ms。 我們在用webpack時一般都會選擇多入口檔案吧,為的就是將自己的原始碼跟第三方庫程式碼分離。這是之前的程式
Web前端效能優化問題
1、請減少HTTP請求基本原理: 在瀏覽器(客戶端)和伺服器發生通訊時,就已經消耗了大量的時間,尤其是在網路情況比較糟糕的時候,這個問題尤其的突出。 一個正常HTTP請求的流程簡述:如在瀏覽器中輸入"www.xxxxxx.com"並按下回車,瀏覽器再與這個URL指向的伺服器建立連線,然後瀏覽器才
淺談前端效能優化(二)——對HTTP傳輸進行壓縮
1、前端效能優化的一點: 對js、css、圖片等進行壓縮,儘可能減小檔案的大小,減少檔案下載的時間,從而減少網頁響應的時間。 2、前端效能優化的另一點: 對HTTP傳輸進行壓縮,即在js,css、圖片等資源已經壓縮的基礎上(其實,檔案的壓縮與否均可,檔案的壓縮跟HTTP傳輸過程的壓縮沒關
淺談前端效能優化(一)
前端效能優化中,減少HTTP請求可以提高頁面的響應速度。 瀏覽器在第一次訪問頁面時向伺服器請求資源,並快取起來,下次再訪問時會判斷在快取中是否已有該資源且有沒有更新過,如果已有該資源且未更新過,則直接從瀏覽器快取中讀取。原理:通過HTTP 請求頭中的 If-Modified-Since(If-No-Matc
如何進行web前端效能優化
這裡是修真院前端小課堂,每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴充套件思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能,本篇分享的是: 【如何進行web前端效能優化】 大家好,我是IT修真院深圳分院第9期的學員