🔝 靜態資源優化的整體思路 | 🔙 上一站 - CSScss
優質的圖片能夠有效吸引用戶,給用戶良好的體驗,因此隨着互聯網的發展,愈來愈多的產品開始使用圖片來提高產品體驗。相較於頁面其餘元素,圖片的體積不容忽視。下圖是截止 2019 年 6 月 HTTP Archive[1] 上統計的網站上各種資源加載的體積:前端
能夠看到,圖片佔據了半壁江山。一樣,在一篇 2018 年的文章中,也提到了圖片在網站中體量的平均佔比已經超過了 50%[2]。然而,隨着平均加載圖片總字節數的增長,圖片的請求數卻再減小,這也說明網站使用的圖片質量和大小正在不斷提升。webpack
因此,若是單純從加載的字節數這個維度來看性能優化,那麼不少時候,優化圖片帶來的流量收益要遠高於優化 JavaScript 腳本和 CSS 樣式文件。下面咱們就來看看,如何優化圖片資源。git
圖片能夠合併麼?固然。最爲經常使用的圖片合併場景就是雪碧圖(Sprite)[3]。github
在網站上一般會有不少小的圖標,不經優化的話,最直接的方式就是將這些小圖標保存爲一個個獨立的圖片文件,而後經過 CSS 將對應元素的背景圖片設置爲對應的圖標圖片。這麼作的一個重要問題在於,頁面加載時可能會同時請求很是多的小圖標圖片,這就會受到瀏覽器併發 HTTP 請求數的限制。我見過一個沒有使用雪碧圖的頁面,首頁加載時須要發送 20+ 請求來加載圖標。將圖標合併爲一張大圖能夠實現「20+ → 1」的巨大縮減。web
雪碧圖的核心原理在於設置不一樣的背景偏移量,大體包含兩點:gulp
background-url
設置爲合併後的雪碧圖的 uri;background-position
來展現大圖中對應的圖標部分。你能夠用 Photoshop 這類工具本身製做雪碧圖。固然比較推薦的仍是將雪碧圖的生成集成到前端自動化構建工具中,例如在 webpack 中使用 webpack-spritesmith,或者在 gulp 中使用 gulp.spritesmith。它們二者都是基於於 spritesmith 這個庫,你也能夠本身將這個庫集成到你喜歡的構建工具中。瀏覽器
咱們知道,通常來講咱們訪問一個頁面,瀏覽器加載的整個頁面實際上是要比可視區域大不少的,也是什麼咱們會提出「首屏」的概念。這就致使其實不少圖片是不在首屏中的,若是咱們都加載的話,至關因而加載了用戶不必定會看到圖片。而圖片體積通常都不小,這顯然是一種流量的浪費。這種場景在一些帶圖片的長列表或者配圖的博客中常常會遇到。緩存
解決的核心思路就是圖片懶加載 —— 儘可能只加載用戶正在瀏覽或者即將會瀏覽到的圖片。實現上來講最簡單的就是經過監聽頁面滾動,判斷圖片是否進入視野,從而真正去加載圖片:安全
function loadIfNeeded($img) {
const bounding = $img..getBoundingClientRect();
if (
getComputedStyle($img).display !== 'none'
&& bounding.top <= window.innerHeight
&& bounding.bottom >= 0
) {
$img.src = $img.dataset.src;
$img.classList.remove('lazy');
}
}
// 這裏使用了 throttle,你能夠實現本身的 throttle,也可使用 lodash
const lazy = throttle(function () {
const $imgList = document.querySelectorAll('.lazy');
if ($imgList.length === 0) {
document.removeEventListener('scroll', lazy);
window.removeEventListener('resize', lazy);
window.removeEventListener('orientationchange', lazy);
return;
}
$imgList.forEach(loadIfNeeded);
}, 200);
document.addEventListener('scroll', lazy);
window.addEventListener('resize', lazy);
window.addEventListener('orientationchange', lazy);
複製代碼
對於頁面上的元素只須要將本來的 src
值設置到 data-src
中便可,而 src
能夠設置爲一個統一的佔位圖。注意,因爲頁面滾動、縮放和橫豎方向(移動端)均可能會改變可視區域,所以添加了三個監聽。
固然,這是最傳統的方法,現代瀏覽器還提供了一個更先進的 Intersection Observer API[4] 來作這個事,它能夠經過更高效的方式來監聽元素是否進入視口。考慮兼容性問題,在生產環境中建議使用對應的 polyfill。
若是想使用懶加載,還能夠藉助一些已有的工具庫,例如 aFarkas/lazysizes、verlok/lazyload、tuupola/lazyload 等。
在使用懶加載時也有一些注意點:
對於佔位圖這塊能夠再補充一點。爲了更好的用戶體驗,咱們可使用一個基於原圖生成的體積小、清晰度低的圖片做爲佔位圖。這樣一來不會增長太大的體積,二來會有很好的用戶體驗。LQIP (Low Quality Image Placeholders)[5] 就是這種技術。目前也已經有了 LQIP 和 SQIP(SVG-based LQIP) 的自動化工具能夠直接使用。
若是你想了解更多關於圖片懶加載的內容,這裏有一篇更詳盡的圖片懶加載指南[6]。
除了對於 <img>
元素的圖片進行來加載,在 CSS 中使用的圖片同樣能夠懶加載,最多見的場景就是 background-url
。
.login {
background-url: url(/static/img/login.png);
}
複製代碼
對於上面這個樣式規則,若是不該用到具體的元素,瀏覽器不會去下載該圖片。因此你能夠經過切換 className 的方式,放心得進行 CSS 中圖片的懶加載。
還有一種方式是將圖片轉爲 base64 字符串,並將其內聯到頁面中返回,即將原 url 的值替換爲 base64。這樣,當瀏覽器解析到這個的圖片 url 時,就不會去請求並下載圖片,直接解析 base64 字符串便可。
可是這種方式的一個缺點在於相同的圖片,相比使用二進制,變成 base64 後體積會增大 33%。而所有內聯進頁面後,也意味着本來可能並行加載的圖片信息,都會被放在頁面請求中(像當因而串行了)。同時這種方式也不利於複用獨立的文件緩存。因此,使用 base64 須要權衡,經常使用於首屏加載 CRP 或者骨架圖上的一些小圖標。
使用合適的圖片格式不只能幫助你減小沒必要要的請求流量,同時還可能提供更好的圖片體驗。
圖片格式是一個比較大的話題,選擇合適的格式[7]有利於性能優化。這裏咱們簡單總結一些。
1) 使用 WebP:
考慮在網站上使用 WebP 格式[8]。在有損與無損壓縮上,它的表現都會優於傳統(JPEG/PNG)格式。WebP 無損壓縮比 PNG 的體積小 26%,webP 的有損壓縮比同質量的 JPEG 格式體積小 25-34%。同時 WebP 也支持透明度。下面提供了一種兼容性較好的寫法。
<picture>
<source type="image/webp" srcset="/static/img/perf.webp">
<source type="image/jpeg" srcset="/static/img/perf.jpg">
<img src="/static/img/perf.jpg">
</picture>
複製代碼
2) 使用 SVG 應對矢量圖場景:
在一些須要縮放與高保真的狀況,或者用做圖標的場景下,使用 SVG 這種矢量圖很是不錯。有時使用 SVG 格式會比相同的 PNG 或 JPEG 更小。
3) 使用 video 替代 GIF:
在兼容性容許的狀況下考慮,能夠在想要動圖效果時使用視頻,經過靜音(muted)的 video 來代替 GIF。相同的效果下,GIF 比視頻(MPEG-4)大 5~20 倍。Smashing Magazine 上有篇文章[9]詳細介紹使用方式。
4) 漸進式 JPEG:
基線 JPEG (baseline JPEG) 會從上往下逐步呈現,相似下面這種:
而另外一種漸進式 JPEG (progressive JPEG)[10] 則會從模糊到逐漸清晰,令人的感覺上會更加平滑。
不過漸進式 JPEG 的解碼速度會慢於基線 JPEG,因此仍是須要綜合考慮 CPU、網絡等狀況,在實際的用戶體驗之上作權衡。
圖片的壓縮通常能夠分爲有損壓縮(lossy compression)和無損壓縮(lossless compression)。顧名思義,有損壓縮下,會損失必定的圖片質量,無損壓縮則可以在保證圖片質量的前提下壓縮數據大小。不過,無損壓縮通常能夠帶來更可觀的體積縮減。在使用有損壓縮時,通常咱們能夠指定一個 0-100 的壓縮質量。在大多數狀況下,相較於 100 質量係數的壓縮,80~85 的質量係數能夠帶來 30~40% 的大小縮減,同時對圖片效果影響較小,即人眼不易分辨出質量效果的差別。
處理圖片壓縮可使用 imagemin 這樣的工具,也能夠進一步將它集成至 webpack、Gulp、Grunt 這樣的自動化工具中。
因爲移動端的發展,屏幕尺寸更加多樣化了。同一套設計在不一樣尺寸、像素比的屏幕上可能須要不一樣像素大小的圖片來保證良好的展現效果;此外,響應式設計也會對不一樣屏幕上最佳的圖片尺寸有不一樣的要求。
以往咱們可能會在 1280px 寬度的屏幕上和 640px 寬度的屏幕上都使用一張 400px 的圖,但極可能在 640px 上咱們只須要 200px 大小的圖片。另外一方面,對於現在盛行的「2 倍屏」、「3 倍屏」也須要使用不一樣像素大小的資源。
好在 HTML5 在 <img>
元素上爲咱們提供了 srcset
和 sizes
屬性,可讓瀏覽器根據屏幕信息選擇須要展現的圖片。
<img srcset="small.jpg 480w, large.jpg 1080w" sizes="50w" src="large.jpg" >
複製代碼
具體的使用方式能夠看這篇文章[11]。
你也許不知道,不少圖片含有一些非「視覺化」的元信息(metadata),帶上它們可會致使體積增大與安全風險[12]。元信息包括圖片的 DPI、相機品牌、拍攝時的 GPS 等,可能致使 JPEG 圖片大小增長 15%。同時,其中的一些隱私信息也可能會帶來安全風險。
因此若是不須要的狀況下,可使用像 imageOptim 這樣的工具來移除隱私與非關鍵的元信息。
在 2.1. 中提到,合適的場景下可使用 SVG。針對 SVG 咱們也能夠進行一些壓縮。壓縮包括了兩個方面:
首先,與圖片不一樣,圖片是二進制形式的文件,而 SVG 做爲一種 XML 文本,一樣是適合使用 gzip 壓縮的。
其次,SVG 自己的信息、數據是能夠壓縮的,例如用相比用 <path>
畫一個橢圓,直接使用 <ellipse>
能夠節省文本長度。關於信息的「壓縮」還有更多能夠優化的點[13]。SVGGO 是一個能夠集成到咱們構建流中的 NodeJS 工具,它能幫助咱們進行 SVG 的優化。固然你也可使用它提供的 Web 服務。
與其餘靜態資源相似,咱們仍然可使用各種緩存策略來加速資源的加載。
圖片做爲現代 Web 應用的重要部分,在資源佔用上一樣也不可忽視。能夠發現,在上面說起的各種優化措施中,同時附帶了相應的工具或類庫。平時咱們主要的精力會放在 CSS 與 JavaScript 的優化上,所以在圖片優化上可能概念較爲薄弱,自動化程度較低。若是你但願更好得去貫徹圖片的相關優化,很是建議將自動化工具引入到構建流程中。
除了上述的一些工具,這裏再介紹兩個很是好用的圖片處理的自動化工具:Sharp 和 Jimp。
好了,咱們的圖片優化之旅就暫時到這了,下面就是字體資源了。
目前內容已所有更新至 ✨ fe-performance-journey ✨ 倉庫中,陸續會將內容同步到掘金上。若是但願儘快閱讀相關內容,也能夠直接去該倉庫中瀏覽。