原創不易,但願能關注下咱們,再順手點個贊~~ |
本文首發於政採雲前端團隊博客: 爲你從新系統梳理下, Web 體驗優化中和圖有關的那些事( 萬字長文)javascript
Web 頁面性能優化,解決了圖片相關,問題就解決了大半。本文從 Web 常見的圖片格式入手,引出與圖片優化相關的有效方案,指望對你們能有一點幫助。css
注:以下說明整理於網絡公開信息。html
有損壓縮:藉由將次要的信息數據捨棄,犧牲一些質量來減小數據量、提升壓縮比。這個過程是不可逆的。圖片常見的有損壓縮手段是合併相近的像素點。前端
無損壓縮:數據通過壓縮後,信息不受損失,還能夠徹底恢復到壓縮前的樣子。無損壓縮技術通常是經過兩個步驟來完成:vue
產生輸入數據的統計模型。java
利用這個統計模型將較常出現的數據用較短的比特序列表示,較不常出現的數據用較長的比特序列表示。react
索引色(Indexed Color):一個字節 2^8 表示一個顏色,也就是最多支持 256 種顏色。webpack
直接色(Direct Color):4 個數字表示一個顏色,分別表示紅、黃、藍以及透明度,因此最多能夠表達 2^32 種顏色。ios
位圖(Bitmap):又叫柵格圖、點陣圖,使用像素陣列來表示圖像。位圖就是由象素陣列的排列來實現其顯示效果的,每一個象素有本身的顏色信息,在對位圖圖像進行編輯操做的時候,可操做的對象是每一個象素,咱們能夠改變圖像的色相、飽和度、明度,從而改變圖像的顯示效果。位圖根據位深度,有 一、四、八、1六、2四、32 位圖像等。位圖放大會失真變模糊。git
矢量圖:計算機圖形學中用點、直線或者多邊形等基於數學方程的幾何圖元表示圖像。相比較位圖,矢量圖保存最少的信息,體積更小,縮放不會失真。
通道:RGB 三種顏色信息稱爲紅通道、綠通道、藍通道,透明度是 Alpha 通道。
JPEG/JPG:使用 JPEG 格式壓縮的圖片文件,是目前應用最普遍的圖片格式之一。最廣泛被使用的擴展名格式爲.jpg,其餘經常使用的擴展名還包括 .jpeg、.jpe、.jfif 以及 .jif。聯合圖像專家小組(Joint Photographic Experts Group)1992 年發佈 JPEG 壓縮標準,1994 年得到 ISO10918-1 認證。其特色是有損壓縮,直接色,位圖,體積最小,經常使用於顏色較豐富、無透明要求的圖片。
PNG:PNG 是一種無損壓縮的位圖圖形格式,支持索引、灰度、RGB 三種顏色方案以及 Alpha 通道等特性。PNG 的開發目標是改善並取代 GIF 做爲適合網絡傳輸的格式而不需專利許可。名稱由來一個是 Portable Network Graphics(便攜式網絡圖形),還有一個非正式的由來是 "Png is Not Gif"。使用場景是帶有透明、半透明背景的圖片,須要在網絡傳輸中顯示預覽效果後展現全貌。上古時期的 IE6 不支持 PNG 半透明,須要用 hack 方法解決。PNG 體積比較大,非必須可用 JPG 替代。PNG 有 png八、png2四、png32 之分。
GIF:圖像互換格式(Graphics Interchange Format)是一種位圖圖形文件格式,無損壓縮、索引色。原始版本爲 87a,1989 年發佈 89a 版本,支持多幀動畫和透明色。1995 年 Netscape Navigator 2.0 發佈,定義了動畫循環多少次或是否無限次播放,如今聊天的動圖都是基於該版本的 GIF。GIF 的特性以下:
WebP:Google 2010 年發佈的同時支持有損和無損壓縮的圖片文件格式。有損用來替換 JPG,無損用來替換PNG,動態用來替換 GIF。根據 Google 較早的測試,WebP 的無損壓縮比網絡上找到的 PNG 檔少了 45% 的文件大小,即便這些 PNG 檔在使用 Pngcrush 和 PNGOUT 處理過,WebP 仍是能夠減小近 30% 的文件體積。WebP 目前還不能全平臺兼容,相關兼容性可點此連接查看。
SVG:可縮放矢量圖形(Scalable Vector Graphics)是一種基於可擴展編輯語言(XML),用於描述二維矢量圖形的圖形格式。SVG 由 W3C 制定,是一個開放標準。SVG 的優勢是文件可讀,易於修改編輯。支持多種濾鏡和特殊效果,在不改變圖像內容的前提下能夠實現位圖中相似文字陰影的效果,還能夠生成動態圖形。
更快、更簡單、更穩定是咱們每個前端工程師的追求,HTTP/2 的出現讓這些美好的詞彙都匯聚在一塊兒。首先來一個 demo 感覺一下牛逼哄哄的 HTTP/2,HTTP/1.1 vs HTTP/2
HTTP/2 全部性能加強的核心在於新的二進制分幀層,它定義瞭如何封裝 HTTP 消息並在客戶端與服務器之間傳輸。
從這張圖比較清晰地看出 HTTP/1.x 和 HTTP/2的區別。HTTP/1.x 協議以換行符做爲純文本的分隔符,而 HTTP/2 將全部傳輸的信息分割爲更小的消息和幀,並採用二進制格式對它們編碼。
通常狀況下,客戶端須要啥東西會告訴服務端,而後服務端返回對應的資源到客戶端。HTTP/2 新增的另外一個強大的新功能是,服務器能夠對一個客戶端請求發送多個響應。換句話說,服務端能夠先於客戶端檢測到將要請求的資源,提早推送到客戶端,不發送全部資源的實體,只發送資源的 URL。客戶端接到後會進行驗證緩存,若是發現須要這些資源,則正式發起請求。
每一個 HTTP 傳輸都承載一組標頭,這些標頭說明了傳輸的資源及其屬性。在 HTTP/1.x 中,這些元數據始終以純文本形式,一般會給每一個傳輸增長 500–800 字節的開銷。若是使用 HTTP Cookie,增長的開銷有時會達到上千字節。 爲了減小此開銷和提高性能,HTTP/2 使用 HPACK 壓縮格式壓縮請求和響應標頭元數據,這種格式採用兩種簡單可是強大的技術:
支持經過靜態霍夫曼代碼對傳輸的標頭字段進行編碼,從而減少了各個傳輸的大小。
客戶端和服務端之間同時維護和更新一個包含以前見過的標頭字段的索引列表(換句話說,它能夠創建一個共享的壓縮上下文),對相同請求而言不須要再次發送和相應,好比 User-Agent。
在 HTTP/2 中,請求和響應標頭字段的定義保持不變,僅有一些微小的差別:全部標頭字段名稱均爲小寫,請求行如今拆分紅各個 :method
、:scheme
、:authority
和 :path
僞標頭字段。
每一個 TCP 鏈接只能發送一個請求, HTTP/1.x 在前面的請求沒有完成前,後面的請求將會阻塞。
其實在 HTTP/2 以前,咱們在寫代碼的時候也用過相似的思想,好比
合併 CSS 和 JS 代碼
雪碧圖
Iconfont
HTTP/2 的出現又可讓咱們省掉很多麻煩。多路複用容許同時經過單一的HTTP請求多個響應。
只加載可視區的內容,當頁面向下滾動時,再繼續加載後面的內容。
圖片懶加載的原理其實很是簡單,咱們先不設置圖片的 src 屬性,將圖片的真實路徑放到一個瀏覽器不認識的屬性中(好比 data-src),而後咱們去監聽 scroll 事件。當頁面的 scrollTop 與瀏覽器的高度之和大於圖片距頁面頂端的 Y (注意是整個頁面不是瀏覽器窗口)時,說明圖片已經進入可視區域,這是把 data-src 的值放到 src 中便可。
關於如何實現本文不作過多闡述,成熟的方案社區比比皆是。這邊推薦幾個比較好用的輪子。
安裝
npm install --save lozad
複製代碼
使用
<img class="lozad" data-src="image.png" />
複製代碼
const observer = lozad(); // 默認會去找 .lozad 這個class
observer.observe();
複製代碼
安裝
npm i vue-lazyload -S
複製代碼
使用
Vue.use(VueLazyload)
複製代碼
用高階組件去包裹
安裝
npm install --save react-lazyload
複製代碼
使用
import React from 'react';
import ReactDOM from 'react-dom';
import LazyLoad from 'react-lazyload';
import MyComponent from './MyComponent';
const App = () => {
return (
<div className="list"> <LazyLoad height={200}> <img src="tiger.jpg" /> </LazyLoad> <LazyLoad> <MyComponent /> </LazyLoad> </div> ); }; ReactDOM.render(<App />, document.body); 複製代碼
緩存是一種保存資源副本並在下次請求時直接使用該副本的技術,所以使用HTTP緩存是WEB性能優化中必不可少的,也是每位前端開發工程師的必修課。
瀏覽器和服務器之間使用的緩存策略能夠分爲強緩存、協商緩存兩種:
有這樣一種場景,瀏覽器檢查本地緩存找到以前響應的文件發現已通過期,只能去服務端請求,可是服務器的資源沒有發生變化,能夠說是浪費了一次請求。
Etag 的出現很好地解決了這個問題,服務器生成並返回的隨機令牌一般是文件內容的哈希值或某個其餘指紋。
客戶端不須要了解指紋是如何生成的,只需在下一次請求時將其發送至服務器。 若是指紋仍然相同,則表示資源未發生變化,您就能夠跳過下載
瀏覽器自動在 If-None-Match
HTTP 請求頭內提供 ETag。 服務器根據當前資源覈對令牌,若是它未發生變化,服務器將返回 304 Not Modified
響應。這樣一個來回避免了瀏覽器再次去請求資源,即省錢又省時間。
Cache-Control是強緩存的一種,每一個資源均可經過 Cache-Control 定義其緩存策略,Cache-Control來控制誰能夠緩存、緩存多久。
無需和服務端通訊的請求是最佳的,經過本地副本消除全部網絡延遲、節省流量。
Cache-Control 是在 HTTP/1.1 規範中定義的,取代了以前用來定義響應緩存策略的頭部(例如 Expires)。 全部現代瀏覽器都支持 Cache-Control,所以,用他就足夠了。
指令 | 說明 |
---|---|
max-age | 指令指定從請求的時間開始,容許提取的響應被重用的最長時間(單位:秒 |
private | 只爲單個用戶緩存,不容許任何中間緩存對其進行緩存 |
no-cache | 先與服務器確認返回的響應是否發生了變化,走協商緩存 |
no-store | 禁止瀏覽器以及全部中間緩存存儲任何版本的返回響應 |
實際開發中每每不存在什麼固定的最優解,咱們須要根據不一樣的業務場景制定相應的策略。
雪碧圖,CSS Sprites,國內也叫 CSS 精靈,是一種 CSS 圖像合成技術,主要用於小圖片顯示。
在網頁中,爲了更好的展示效果,常常會採用一些小圖標來替代文字。經常使用的方式包括 Icon Fonts、SVG Icons、小圖片,其中 Icon Fonts 只支持單色,SVG Icons 需 IE9+。
注:如不考慮低版本瀏覽器兼容性,推薦使用 SVG Icons,有興趣能夠閱讀 張鑫旭-SVG Sprites技術介紹
若是採用小圖片,須要考慮一個問題:每張小圖片獨立請求,加載時都會產生一個 HTTP 請求,而小圖片體積(1 ~ 2 kb)就比較小,對比上 HTTP 請求鏈接、請求頭內容、響應等的開銷,就顯得很是不必了,那有沒有可能將多張小圖片合併成一張圖?
將小圖標合併成一張圖片,利用 backround-position
屬性值來肯定圖片呈現的位置便可。以下圖所示不一樣尺寸、位置:
經過 CSS 定位,能夠展示對應的圖標。
.icon-alipay {
background-image: url(sprite.png);
background-position: 0px -131px;
width: 81px;
height: 73px;
}
.icon-taobao {
background-image: url(sprite.png);
background-position: -177px 0px;
width: 114px;
height: 114px;
}
.icon-wechat {
background-image: url(sprite.png);
background-position: 0px 0px;
width: 177px;
height: 131px;
}
.icon-xinlang {
background-image: url(sprite.png);
background-position: -81px -131px;
width: 72px;
height: 72px;
}
複製代碼
每次修改或者新增圖片時,都須要對雪碧圖進行修改,若是依靠人工維護,成本過高,可否自動生成雪碧圖和樣式呢?可使用 spritesmith,該工具可自動合併圖片,並獲得圖片在合併以後的相對位置。簡單使用示例代碼以下:
const fs = require('fs')
const path = require('path');
const Spritesmith = require('spritesmith');
const baseDir = './images';
const files = fs.readdirSync(baseDir)
const sprites = files.map(file => path.join(baseDir, file))
Spritesmith.run({src: sprites}, (err, result) => {
if (err) {
console.error(err)
} else {
console.info(result);
}
})
複製代碼
運行的輸出結果以下:
{
coordinates: {
'images/alipay.png': { x: 0, y: 131, width: 81, height: 73 },
'images/taobao.png': { x: 177, y: 0, width: 114, height: 114 },
'images/wechat.png': { x: 0, y: 0, width: 177, height: 131 },
'images/xinlang.png': { x: 81, y: 131, width: 72, height: 72 }
},
properties: { width: 291, height: 204 },
image: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 01 23 00 00 00 cc 08 06 00 00 00 38 45 c5 ce 00 00 40 06 49 44 41 54 78 01 ec c1 0b 98 e6 05 61 ... 22705 more bytes>
}
複製代碼
其中
coordinates
:每張圖片對應的尺寸和位置
properties
:生成的圖片尺寸
image
:文件的Buffer,可用於生成圖片
對於現有的經常使用的構建工具,已有現成的插件可直接使用:
如下爲 gulp
配合 gulp.spritesmith
的示例代碼:
const gulp = require('gulp');
const spritesmith = require('gulp.spritesmith');
// gulp任務定義
gulp.task('sprite', () => {
return gulp.src('images/*.png')
.pipe(spritesmith({
imgName: 'sprite.png',
cssName: 'sprite.css'
})
).pipe(gulp.dest('./'));
});
複製代碼
運行後會獲得sprite.png
、sprite.css
兩個文件,最後打包的時候將文件對應打包進去便可。
background-size
屬性來使得最終顯示正常,可參考以上插件的retina
相關配置參數;iconfont 譯爲字體圖標,即經過字體的方式展現圖標,多用於渲染圖標、簡單圖形、特殊字體等。
使用 iconfont 時,因爲只須要引入對應的字體文件,針對加載圖片張數較多的狀況,可有效減小 HTTP 請求次數,並且通常字體體積較小,因此請求傳輸數據量較少。與直接引入圖片不一樣,iconfont 能夠像使用字體同樣,設置大小和顏色,還能夠經過 CSS 設置特殊樣式,且由於其是矢量圖,不存在失真的狀況。
那麼,怎麼使用 iconfont 呢?請參照如下 demo:
根據開發需求,按需引入不一樣格式的字體文件(eot / ttf / woff / svg)
<style>
@font-face {
font-family: "iconfont";
src: url('iconfont.eot'); /* IE9*/
src: url('iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfont.woff') format('woff'),
url('iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont";
}
</style>
<body>
//  是一個字符的 unicode 碼,在該 iconfont 字體文件中對應某個圖標
<i class="iconfont"></i>
</body>
複製代碼
關於 @font-face 的說明可參考 mozilla 官方文檔。
在平時開發工做中,可以使用如下經常使用圖標字體庫:
Base64 是網絡上最多見的用於傳輸 8Bit 字節碼的編碼方式之一,可將圖片編碼爲特定的字符串,由 52 個大小字母和 10 個數字,以及 +、/、= 三個字符組成,詳見 wiki.
使用 Base64 編碼渲染圖片有如下優勢:
凡事皆有利弊,使用 Base64 編碼同時也會帶來一些問題:
如需將圖片轉換爲 Base64 編碼,可使用 JavaScript API FileReader.readAsDataURL
,詳細可參考文檔。
圖片體積壓縮就是在圖片保持在可接受的清晰度範圍內同時減小文件大小,圖片體積壓縮能夠藉助工具實現。
阿里雲 OSS 能夠經過配置參數形式對圖片進行處理,支持縮放、裁剪、旋轉、效果、格式轉換、水印等操做,詳細信息點此 文檔 查看。
示例:將圖強制縮略成寬度爲 100,高度爲 100。
http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,m_fixed,h_100,w_100
複製代碼
本文做者之一@明明 亦作過一個小工具,經過配置縮放參數、壓縮質量、格式等屬性後自動生成 OSS 後綴地址,具體如何使用參考文檔。
七牛雲的圖片處理服務和阿里雲 OSS 功能相似,也能夠對圖片進行縮放、裁剪、旋轉、縮略等操做,詳細文檔
示例:將圖強制縮略成寬度爲 100,高度爲 100。
https://odum9helk.qnssl.com/resource/Ship.jpg?imageView2/2/w/100/h/100
複製代碼
Web 開發中常見的圖片包括 JPG,PNG,GIF,webP,選擇合適的格式以及壓縮質量能夠在保證視覺效果的狀況下,加速網站的呈現。下面針對不一樣圖片格式的特性來作一下對比:
類型 | 動畫 | 壓縮類型 | 瀏覽器支持 | 透明度 |
---|---|---|---|---|
GIF | 支持 | 無損壓縮 | 全部 | 支持 |
PNG | 不支持 | 無損壓縮 | 全部 | 支持 |
JPEG | 不支持 | 有損壓縮 | 全部 | 不支持 |
webP | 不支持 | 無損壓縮或有損壓縮 | Chrome、Opera、Firefox、Edge、Android | 支持 |
回憶本文開頭介紹的不一樣圖片格式的特色,你們能夠參考下圖選擇合適的使用場景:
在 Retina 視網膜屏幕面世以前人們不多關注像素密度與設備像素比,隨着 Retina 屏在移動設備中愈來愈普遍地應用,爲了保證圖片在不一樣 DPR(設備像素比)的設備上顯示足夠清晰,開發者須要針對不一樣設備適配不一樣倍數的圖片。
window.devicePixelRatio
來獲取。瞭解以上的概念,咱們不難理解:
在 DPR 爲 1 的設備(一倍屏)上,使用 1 * 1 個物理像素展現一個 CSS 像素。
在 DPR 爲 2 的設備(二倍屏)上,使用 2 * 2 個物理像素展現一個 CSS 像素。
在 DPR 爲 3 的設備(三倍屏)上,使用 3 * 3 個物理像素展現一個 CSS 像素。
針對一張 30px * 30px 的圖片,在 1 倍屏上,按照 1 : 1 平鋪,圖片質量並不損失。可是在 2 倍屏、3 倍屏上,分別經過 60 * 60 與 90 * 90 個物理像素渲染這張圖片就會出現模糊、失真的現象,從而影響用戶體驗。因此,咱們須要根據不一樣 DPR 去加載不一樣倍數的圖片:
在高分辨率顯示屏如 2x 上,在頁面中使用二倍圖能夠保證清晰度,可是當此頁面在低 DPR 設備打開時,咱們只須要 50% 長寬的圖片就能保證顯示效果,而此時帶寬開銷倒是同樣的。因此爲了節約傳輸流量,咱們須要告訴瀏覽器,根於不一樣的 DPR 加載不一樣尺寸的圖片,一般有如下三種方法:
<picture>
<source srcset="photo@3x.jpg" media="(min-width: 800px)">
<source srcset="photo@2x.jpg" media="(min-width: 600px)">
<img srcset="photo.jpg">
</picture>
複製代碼
<img src="photo.png" srcset="photo@2x.png 2x, photo@3x.png 3x" alt="photo" />
複製代碼
background-image: image-set("photo.png" 1x,
"photo@2x.png" 2x,
"photo@3x.png" 3x);
複製代碼
根據世界健康組織的統計,全球約有 2.85 億視力障礙人士,僅美國就有 810 萬網民患視力障礙,而在中國,這個數字要被乘以 2。不一樣於咱們瀏覽網頁的方式是看,視力障礙人員瀏覽互聯網信息主要是靠聽 —— 靠屏幕閱讀器讀出網頁的有效信息,經過聽這些信息來獲知內容。對於前端工程師而言,關注到一些細節的優化,能更好的服務於這些視力障礙人士。
最基礎的方式,是裝飾性圖片歸類到背景圖,經過 CSS 背景圖進行設置;功能性圖片放到 HTML 中,經過 img
標籤引入,且要設置 alt
屬性,以便被屏幕閱讀器識別並閱讀。圖片 alt
信息應簡短,介紹圖片信息便可,避免內容冗餘。圖片的長信息介紹應被放到 longdesc
屬性中:
<img src="" alt="圖片說明" />
<img src="" longdesc="一段很長的文字一段很長的文字一段很長的文字一段很長的文字" />
複製代碼
更多無障礙相關,可參考《騰訊網無障礙說明》。
「高對比度模式」 是一種 Windows 系統的設置主題,其用意是爲了保證視力受損的用戶,在查看 Web 信息時提供方便。它經過使用對比鮮明的色彩和字號來提升文本的可讀性,高對比度模式下網頁的背景默認會變成全黑。
CSS Image Sprites(CSS 雪碧圖)是一項用來減小網頁中圖片 HTTP 請求數的技術,但其會致使在 Windows 高對比度模式下背景圖片消失,其服務的 Web 應用性能的提高和對無障礙體驗被破壞之間的矛盾,已經引發了 Web 開發者的關注。Sprite 技術的使用的確爲更多網站的優化加載速度的體驗貢獻甚大,但咱們要認可,這個過程當中咱們忽略並損害了使用高對比度模式用戶的體驗。因爲 <img>
元素能夠在高對比度模式下顯示,故而在此場景下,使用基於 <img>
標籤的 Sprite 技術,能夠獲得比基於 CSS 背景圖的 Sprite 更多的收益。
關於 IMG Sprite 技術的應用,能夠在此文中學習到 《Foreground Sprites – High Contrast Mode Optimization》
就前端性能優化而言,圖片優化可謂是其必不可少的環節。可是與其說是在作優化,不如說是在作「權衡」。一些操做是以犧牲一部分紅像質量爲代價的。咱們的主要任務,是儘量的去尋求一個質量與性能之間的平衡點,並在不一樣業務場景下,作好圖片方案的選型工做。