本文首發於政採雲前端團隊博客 : 爲你從新系統梳理下, Web 體驗優化中和圖有關的那些事(萬字長文)
Web 頁面性能優化,解決了圖片相關,問題就解決了大半。本文從 Web 常見的圖片格式入手,引出與圖片優化相關的有效方案,指望對你們能有一點幫助。javascript
注:以下說明整理於網絡公開信息。
無損壓縮:數據通過壓縮後,信息不受損失,還能夠徹底恢復到壓縮前的樣子。無損壓縮技術通常是經過兩個步驟來完成:css
PNG:PNG 是一種無損壓縮的位圖圖形格式,支持索引、灰度、RGB 三種顏色方案以及 Alpha 通道等特性。PNG 的開發目標是改善並取代 GIF 做爲適合網絡傳輸的格式而不需專利許可。名稱由來一個是 Portable Network Graphics(便攜式網絡圖形),還有一個非正式的由來是 "Png is Not Gif"。使用場景是帶有透明、半透明背景的圖片,須要在網絡傳輸中顯示預覽效果後展現全貌。上古時期的 IE6 不支持 PNG 半透明,須要用 hack 方法解決。PNG 體積比較大,非必須可用 JPG 替代。PNG 有 png八、png2四、png32 之分。html
GIF:圖像互換格式(Graphics Interchange Format)是一種位圖圖形文件格式,無損壓縮、索引色。原始版本爲 87a,1989 年發佈 89a 版本,支持多幀動畫和透明色。1995 年 Netscape Navigator 2.0 發佈,定義了動畫循環多少次或是否無限次播放,如今聊天的動圖都是基於該版本的 GIF。GIF 的特性以下:前端
更快、更簡單、更穩定是咱們每個前端工程師的追求,HTTP/2 的出現讓這些美好的詞彙都匯聚在一塊兒。首先來一個 demo 感覺一下牛逼哄哄的 HTTP/2,HTTP/1.1 vs HTTP/2vue
HTTP/2 全部性能加強的核心在於新的二進制分幀層,它定義瞭如何封裝 HTTP 消息並在客戶端與服務器之間傳輸。java
從這張圖比較清晰地看出 HTTP/1.x 和 HTTP/2的區別。HTTP/1.x 協議以換行符做爲純文本的分隔符,而 HTTP/2 將全部傳輸的信息分割爲更小的消息和幀,並採用二進制格式對它們編碼。react
通常狀況下,客戶端須要啥東西會告訴服務端,而後服務端返回對應的資源到客戶端。HTTP/2 新增的另外一個強大的新功能是,服務器能夠對一個客戶端請求發送多個響應。換句話說,服務端能夠先於客戶端檢測到將要請求的資源,提早推送到客戶端,不發送全部資源的實體,只發送資源的 URL。客戶端接到後會進行驗證緩存,若是發現須要這些資源,則正式發起請求。webpack
每一個 HTTP 傳輸都承載一組標頭,這些標頭說明了傳輸的資源及其屬性。在 HTTP/1.x 中,這些元數據始終以純文本形式,一般會給每一個傳輸增長 500–800 字節的開銷。若是使用 HTTP Cookie,增長的開銷有時會達到上千字節。 爲了減小此開銷和提高性能,HTTP/2 使用 HPACK 壓縮格式壓縮請求和響應標頭元數據,這種格式採用兩種簡單可是強大的技術:ios
在 HTTP/2 中,請求和響應標頭字段的定義保持不變,僅有一些微小的差別:全部標頭字段名稱均爲小寫,請求行如今拆分紅各個 :method
、:scheme
、:authority
和 :path
僞標頭字段。git
每一個 TCP 鏈接只能發送一個請求, HTTP/1.x 在前面的請求沒有完成前,後面的請求將會阻塞。
其實在 HTTP/2 以前,咱們在寫代碼的時候也用過相似的思想,好比
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的出現很好地解決了這個問題,其爲一個哈希值,瀏覽器甚至不用去關係這個值是怎麼來的,在第一次請求時,瀏覽器生成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
來獲取。瞭解以上的概念,咱們不難理解:
針對一張 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》
就前端性能優化而言,圖片優化可謂是其必不可少的環節。可是與其說是在作優化,不如說是在作「權衡」。一些操做是以犧牲一部分紅像質量爲代價的。咱們的主要任務,是儘量的去尋求一個質量與性能之間的平衡點,並在不一樣業務場景下,作好圖片方案的選型工做。
政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 50 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。
若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com