## 一、圖像
**1)響應式圖像**
  瀏覽器根據屏幕大小、設備像素比、橫豎屏自動加載合適的圖像。
  [響應式的功能](https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images)可以通過srcset和sizes兩個新屬性實現。
  前者可指定選擇的圖像以及其大小,后者會定義一組媒體條件并聲明填充的寬度。
  在下面的示例中([在線查看效果](https://mdn.github.io/learning-area/html/multimedia-and-embedding/responsive-images/responsive.html)),瀏覽器會先查看設備寬度,然后檢查sizes列表中哪個媒體條件第一個為真,再查看該媒體查詢的填充寬度,最后從加載的srcset列表中引用寬度最接近的圖像。
~~~html
<img srcset="elva-fairy-320w.jpg 320w,
elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="elva-fairy-800w.jpg" />
~~~
**2)懶加載**
  [懶加載](https://www.cnblogs.com/strick/p/5372694.html)就是當滾動到頁面某個位置后,再顯示當前位置的圖像,這樣做可以減少頁面請求。
  預加載就是通過Image對象,給這個對象添加src屬性,以JavaScript的方式請求圖像,并可以緩存此對象,以后再用。
**3)漸進加載**
  漸進加載是指先加載低質量甚至模糊的圖片,然后隨著頁面繼續加載,使用[LQIP](https://github.com/zouhir/lqip)(低質量圖片占位符)技術替換為高質量的完整版本。
  這種技術確實提高了首次進行有意義繪制的時間。
**4)壓縮**
  除了可通過剝離元數據(例如時間、地點等)的方式壓縮圖像之外,還可以將圖像畫到Canvas中,然后選擇質量輸出,從而實現壓縮。
**5)雪碧圖**
  CSS Sprite是一種圖像處理技術,將零散的小圖標整合在一起,形成成一張大圖,這張圖可稱為雪碧圖或精靈圖。
  當用這張大圖做背景圖像時,可以利用background-position屬性進行背景定位,找到想要的小圖標。
  這么處理圖像,不但可以解決命名困擾,還能減少HTTP請求數,降低圖像字節,提升網頁性能。
  參照下面的最佳實踐,可將雪碧圖優化得盡可能小:
1. 按照顏色合并。
2. 避免不必要的空白。
3. 將元素水平排列。
4. 將顏色限制在256種以內。
5. 先優化單獨的圖像,再優化雪碧圖。
6. 通過控制大小和對齊減少反鋸齒像素的數量。
7. 避免使用對角線漸變,這種漸變無法被平鋪。
**6)WebP**
  [WebP](https://developers.google.com/speed/webp)由Google引入,是一種支持有損壓縮和無損壓縮的圖片文件格式,派生自圖像編碼格式VP8。
  根據Google的測試,無損壓縮后的WebP比PNG文件少了45%的文件大小,即使這些PNG文件經過其他壓縮工具壓縮之后,WebP還是可以減少28%的文件大小。
  WebP不支持像JPEG那樣的漸進式渲染,這導致用戶使用好的JPEG可能會更快地看到實際圖像,盡管WebP圖像的網絡加載速度可能會更快。
**7)icon font**
  [圖標字體](https://www.iconfont.cn/)(icon font)就是將圖標做成字體,使用時與普通字體無異。
  換成字體后,顏色、陰影、翻轉、大小和對齊等功能都能用CSS屬性控制,比圖像靈活很多,并且縮放還不會失真,同時兼容IE6,并且生成的文件特別小。
  使用[font-display](https://developer.mozilla.org/zh-CN/docs/Web/CSS/@font-face/font-display)這個CSS屬性來控制字體的加載行為并使內容立即(font-display: optional)或幾乎立即(font-display: swap)可讀。
  字體加載[API](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/fonts)用JavaScript來控制如何加載字體,提供了很大的自由度來決定如何將字體應用于文檔。
~~~
document.fonts.load("1em Open Sans Light"); //加載字體
document.fonts.load("1em Open Sans Bold");
// 在所有指定的字體都加載后運行
document.fonts.ready.then(function(fontFaceSet) { });
~~~
## 二、網絡
**1)GZip壓縮**
  通過添加HTTP首部來向Web服務器聲明支持壓縮。
~~~
Accept-Encoding: gzip, deflate
~~~
  Web服務器會返回一個經過壓縮的響應。
~~~
Content-Encoding: gzip
~~~
  GZip對于要壓縮的文件,首先使用LZ77算法,然后對得到的結果再使用Huffman編碼的方法進行壓縮。
  LZ77算法過程如下:
1. 如果文件中有兩塊內容相同的話,那么只要知道前一塊的位置和大小,就可以確定后一塊的內容。
2. 可以用這樣一對信息(兩者之間的距離,相同內容的長度),來替換后一塊內容。
3. 由于這一對信息的大小,小于被替換內容的大小,所以文件得到了壓縮。
Huffman編碼過程如下:
1. 把文件中一定位長的值看作是符號,例如把8位長的256種值,也就是字節的256種值看作是符號。
2. 根據這些符號在文件中出現的頻率,對這些符號重新編碼。
3. 對于出現次數非常多的,用較少的位來表示,對于出現次數非常少的,用較多的位來表示。
4. 這樣一來,文件的一些部分位數變少了,一些部分位數變多了。
5. 由于變小的部分比變大的部分多,所以整個文件的大小還是會減小,文件得到了壓縮。
**2)Brotli壓縮**
  2015年,Google推出了Brotli,這是一種全新的開源無損數據格式,并被現在所有現代瀏覽器支持。
  盡管Brotli在某些方面的性能與GZip相當,但它顯示出了良好的前景,并在不斷地發展中。
  如果瀏覽器支持Brotli,那么在請求首部中會將br令牌包含在可接受的編碼列表中。
~~~
Accept-Encoding: gzip, deflate, br
~~~
  GZip的壓縮級別可指定0~9的整數來配置,而Brotli的范圍是0~11。
  注意,使用Brotli壓縮所有資源非常耗費計算資源和時間,在最高壓縮級別下,會讓服務器等待動態資源,服務器開始發送響應所花費的時間會抵消文件大小減少帶來的任何潛在收益。
  如果可以避開動態壓縮靜態資源的成本,那么Brotli就是值得的。
  Brotli可用于任何純文本的內容(HTML、CSS、SVG、JavaScript等),并且其性能相比GZip提高了17-25%。
**3)DNS預讀取**
  DNS預讀取技術能夠加快打開速度,方法是在head元素里寫上幾個link元素。
~~~html
<link rel="dns-prefetch" href="http://www.pwstrick.com">
<link rel="dns-prefetch" href="http://www.home602.com">
~~~
  對以上幾個網站提前解析DNS,由于是并行的,也就不會阻塞頁面渲染。
**4)緩存**
  緩存的處理過程可以簡單的分為幾步,首先在緩存中搜索指定資源的副本,如果命中就執行第二步;第二步就是對資源副本進行新鮮度檢測(也就是文檔是否過期),如果不新鮮就執行第三步;第三步是與服務器進行再驗證,驗證通過(即沒有過期)就更新資源副本的新鮮度,再返回這個資源副本(此時的響應狀態碼為“304 Not Modified”),不通過就從服務器返回資源,再將最新資源的副本放入緩存中。
  HTTP緩存分為強緩存和協商緩存,前者的首部包括Cache-Control和Expires;后者會分為日期比對法(If-Modified-Since和Last-Modified)和實體標記法(If-None-Match和ETag)。
**5)CDN**
  CDN是在用戶和服務器之間增加Cache層,將用戶的訪問請求引導到Cache節點而不是服務器源站點,要實現這一目的,主要是通過接管DNS實現。
  CDN可以在全球分發各類資源,并根據地理位置、接入網類型(電信還是網通)將用戶的訪問請求定位到離用戶路由最短、位置最近、負載最輕的Cache節點(緩存服務器)上,實現就近定位,以此提升性能。
**6)并發請求**
  瀏覽器的并發請求數目限制是針對同一域名的。
  同一時間針對同一域名下的請求有一定數量限制(例如Chrome限制6條、IE11限制13條等),超過限制數目的請求會被阻塞。
  所以將不同靜態資源(例如圖片、JavaScript、CSS等)放在不同的域名下,就能增加并發請求的數量。
**7)HTTP/2.0**
  HTTP/2.0引入了全新的二進制分幀層,解決了隊首阻塞問題,并且在一個域只需建立一次TCP連接,就能實現多路通信,不必進行資源排隊,這樣就不需要雪碧圖或合并文件了。
  不僅如此,HTTP/2.0還有控制請求優先級、服務器主動推送和首部壓縮等優點,
**8)API性能優化**
  在設計和開發API時,需要一個合理的協議來實現服務器與第三方請求之間的通信。
  Restful API(REST)是一種廣泛驗證有效的選擇:它定義了一組約束,開發人員可以遵循這些約束以使內容以高性能,可靠和可伸縮的方式進行訪問。符合REST約束定義的Web服務稱為RESTful Web服務。
  與普通的HTTP請求一樣,當從API檢索數據時,服務器響應中的任何延遲都將傳播到最終用戶,從而影響渲染。如果許多資源都需要來自某個API的數據,則該API可能會成為性能瓶頸。GraphQL為這些問題提供了高性能的解決方案。
  與REST不同,GraphQL可以在單個請求中檢索所有數據,并且只響應所需的內容,而不會像REST通常那樣過度或不足地獲取數據。
  此外,因為GraphQL使用模式(Schema,定義數據結構的元數據),所以它可以提前將數據組織成所需的結構。因此在使用GraphQL后,就可以刪除用于處理狀態和數據結果的JavaScript代碼,從而產生在客戶端上運行更快更干凈的應用程序代碼。
**9)關鍵資源**
  把那些能阻塞網頁首次渲染的資源稱為關鍵資源,例如首次請求的 JavaScript、HTML和CSS等。
  基于關鍵資源可細化出三個優化頁面首次渲染的原則。
1. 第一個是減少關鍵資源個數。
2. 第二個是降低關鍵資源大小。
3. 第三個是減少請求關鍵資源的 RTT 次數。
  RTT(Round Trip Time)就是往返時間,即數據發送時刻到接收到確認時刻的差值。
*****
> 原文出處:
[博客園-Web優化躬行記](https://www.cnblogs.com/strick/category/1795726.html)
[知乎專欄-Web優化躬行記](https://zhuanlan.zhihu.com/c_1260996761008627712)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎閱讀。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020