本文在github作了收錄 https://github.com/Michael-lz... javascript
在類電商類項目,每每存在大量的圖片,如 banner 廣告圖,菜單導航圖,美團等商家列表頭圖等。圖片衆多以及圖片體積過大每每會影響頁面加載速度,形成不良的用戶體驗,因此對圖片進行優化勢在必行。css
咱們先來看一個頁面啓動時加載的圖片信息。html
如圖所示,這個頁面啓動時加載了幾十張圖片(甚至更多),而這些圖片請求幾乎是併發的,在 Chrome 瀏覽器,最多支持的併發請求次數是有限的,其餘的請求會推入到隊列中等待或者停滯不前,直到上輪請求完成後新的請求才會發出。因此至關一部分圖片資源請求是須要排隊等待時間的,過多的圖片必然會影響頁面的加載和展現。java
JPEG 是由 Joint Photographic Experts Group 所開發出的一種圖片。它最大的特色是 有損壓縮。這種高效的壓縮算法使它成爲了一種很是輕巧的圖片格式。另外一方面,即便被稱爲「有損」壓縮,JPG 的壓縮方式仍然是一種高質量的壓縮方式:當咱們把圖片體積壓縮至原有體積的 50% 如下時,JPG 仍然能夠保持住 60% 的品質。此外,JPG 格式以 24 位存儲單個圖,能夠呈現多達 1600 萬種顏色,足以應對大多數場景下對色彩的要求,這一點決定了它壓縮先後的質量損耗並不容易被咱們人類的肉眼所察覺。webpack
優勢css3
使用場景git
PNG(可移植網絡圖形格式)是由 W3C 開發的圖片格式,是一種無損壓縮的高保真的圖片格式。它同時支持 8 位和 24 位,這裏都是二進制數的位數。按照咱們前置知識裏提到的對應關係,8 位的 PNG 最多支持 256 種顏色,而 24 位的能夠呈現約 1600 萬種顏色。github
PNG 圖片具備比 JPEG 更強的色彩表現力,對線條的處理更加細膩,對透明度有良好的支持。它彌補了上文咱們提到的 JPEG 的侷限性,惟一的缺點就是 體積太大。web
應用場景算法
GIF 是一種最多支持 256 種顏色的 8 位無損圖片格式。這個限制讓 GIF 格式對於多顏色或者攝影圖片的展現無能爲力。
優勢
應用場景
WebP 是一種同時提供了有損壓縮與無損壓縮(可逆壓縮)的圖片文件格式,派生自影像編碼格式 VP8。它像 JPEG 同樣對細節豐富的圖片信手拈來,像 PNG 同樣支持透明,像 GIF 同樣能夠顯示動態圖片,集多種圖片文件格式的優勢於一身。
WebP 最初在 2010 年發佈,目標是減小文件大小,但達到和 JPEG 格式相同的圖片質量,但願可以減小圖片檔在網絡上的發送時間。根據 Google 較早的測試,WebP 的無損壓縮比網絡上找到的 PNG 檔少了 45%的文件大小,即便這些 PNG 檔在使用 pngcrush 和 PNGOUT 處理過,WebP 仍是能夠減小 28%的文件大小。
雖然 webP 有諸多優勢,可是它不能徹底替代 JPEG 和 PNG,由於瀏覽器對 WebP 支持並不廣泛。特別是移動端 IOS 系統基本不支持。
咱們再來看一下一張圖片的加載過程:
圖片衆多以及圖片體積過大每每會影響頁面加載速度,形成不良的用戶體驗,有部分圖片達到幾百 kB,甚至 2M(這鍋必須運營背,非得上傳高清大圖不可?),直接致使了加載時間過長。因此對於體積過大的圖片,在保持圖片在可接受的清晰度範圍內可適當對圖片大小進行壓縮。
圖片壓縮又分爲有損壓縮和無損壓縮。
有損壓縮
有損壓縮指在壓縮文件大小的過程當中,損失了一部分圖片的信息,也即下降了圖片的質量(即圖片被壓糊了),而且這種損失是不可逆的。常見的有損壓縮手段是按照必定的算法將臨近的像素點進行合併。壓縮算法不會對圖片全部的數據進行編碼壓縮,而是在壓縮的時候,去除了人眼沒法識別的圖片細節。所以有損壓縮能夠在同等圖片質量的狀況下大幅下降圖片的體積。例如 jpg 格式的圖片使用的就是有損壓縮。
無損壓縮
無損壓縮指的是在壓縮圖片的過程當中,圖片的質量沒有任何損耗。咱們任什麼時候候均可以從無損壓縮過的圖片中恢復出原來的信息。壓縮算法對圖片的全部的數據進行編碼壓縮,能在保證圖片的質量的同時下降圖片的體積。例如 png、gif 使用的就是無損壓縮。
下面是各類圖片格式的壓縮類型
工程化的項目能夠在 webpack 裏面配置 image-webpack-loader 進行圖片壓縮
npm install --save-dev image-webpack-loader
module.exports = { ... module: { rules: [ { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use: [ { loader: 'file-loader', options: { name: '[name].[hash:7].[ext]' }, }, { loader: 'image-webpack-loader', options: { mozjpeg: { progressive: true, quality: 50, }, optipng: { enabled: true, }, pngquant: { quality: [0.5, 0.65], speed: 4, }, gifsicle: { interlaced: false, }, webp: { // 不支持WEBP就不要寫這一項 quality: 75 }, }, }, ], }, ], }, }
至於要不要使用插件自動壓縮就見仁見智了,由於有些 UI 和產品會說壓縮出來的效果圖片不是他們想要的。
雪碧圖,CSS Sprites,國內也叫 CSS 精靈,是一種 CSS 圖像合成技術,主要用於小圖片顯示。
瀏覽器請求資源的時候,同源域名請求資源的時候有最大併發限制,chrome 爲 6 個,就好比你的頁面上有 10 個相同 CDN 域名小圖片,那麼須要發起 10 次請求去拉取,分兩次併發。第一次併發請求回來後,發起第二次併發。若是你把 10 個小圖片合併爲一張大圖片的畫,那麼只用一次請求便可拉取下來 10 個小圖片的資源。減小服務器壓力,減小併發,減小請求次數。
優勢
把諸多小圖片合成一張大圖,利用 backround-position
屬性值來肯定圖片呈現的位置,能夠有效的較少請求個數,並且,而不影響開發體驗,使用構建插件能夠作到對開發者透明。適用於頁面圖片多且豐富的場景。
缺點
生成的圖片體積較大,減小請求個數同時也增長了圖片大小,不合理拆分將不利於並行加載。
合成雪碧圖
在 webpack 中,有相應的插件提供了自動合成雪碧圖的功能而且能夠自動生成對應的樣式文件—— webpack-spritesmith,使用方法以下
var path = require('path') var SpritesmithPlugin = require('webpack-spritesmith') module.exports = { // ... plugins: [ new SpritesmithPlugin({ src: { cwd: path.resolve(__dirname, 'src/ico'), glob: '*.png', }, target: { image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'), css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl'), }, apiOptions: { cssImageRef: '~sprite.png', }, }), ], }
經過上面配置就能將 src/ico
目錄下的全部 png 文件合成雪碧圖,而且輸出到對應目錄,同時還能夠生成對應的樣式文件,樣式文件的語法會根據你配置的樣式文件的後綴動態生成。
iconfont(字體圖標),即經過字體的方式展現圖標,多用於渲染圖標、簡單圖形、特殊字體等。
優勢
推薦使用阿里的字體圖標庫:iconfont
原理:將圖片轉換爲 base64 編碼字符串 inline 到頁面或 css 中。
優勢
但須要注意的是:若是圖片較大,圖片的色彩層次比較豐富,則不適合使用這種方式,由於該圖片通過 base64 編碼後的字符串很是大,會明顯增大 HTML 頁面的大小,從而影響加載速度。
base64 化最多見的就是在 url-loader 中使用。
module.exports = { ... module: { rules: [ { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10240, name: utils.assetsPath('img/[name].[hash:7].[ext]'), } }, ], }, }
這樣就能將項目中小於 10kb 的圖片轉化爲 base64 應用到頁面中
好比實現修飾效果,如半透明、邊框、圓角、陰影、漸變等,在當前主流瀏覽器中均可以用 CSS 達成,這樣能減小圖片的請求,達到優化的目的。
缺點
CDN 的全稱是 Content Delivery Network,即內容分發網絡。CDN 是構建在網絡之上的內容分發網絡,依靠部署在各地的邊緣服務器,經過中心平臺的負載均衡、內容分發、調度等功能模塊,使用戶就近獲取所需內容,下降網絡擁塞,提升用戶訪問響應速度和命中率。CDN 的關鍵技術主要有內容存儲和分發技術。
舉個簡單的例子:
之前買火車票你們都只能去火車站買,後來咱們買火車票就能夠在樓下的火車票代售點買了。
基本原理
CDN 的基本原理是普遍採用各類緩存服務器,將這些緩存服務器分佈到用戶訪問相對集中的地區或網絡中,在用戶訪問網站時,利用全局負載技術將用戶的訪問指向距離最近的工做正常的緩存服務器上,由緩存服務器直接響應用戶請求。
基本思路
CND 的基本思路是儘量避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。經過在網絡各處放置節點服務器所構成的在現有的互聯網基礎之上的一層智能虛擬網絡,CDN 系統可以實時地根據網絡流量和各節點的鏈接、負載情況以及到用戶的距離和響應時間等綜合信息將用戶的請求從新導向離用戶最近的服務節點上。其目的是使用戶可就近取得所需內容,解決 Internet 網絡擁擠的情況,提升用戶訪問網站的響應速度。
CDN 的優點
懶加載是一種網頁性能優化的方式,它能極大的提高用戶體驗。圖片一直是影響網頁性能的主要元兇,如今一張圖片超過幾兆已是很常常的事了。若是每次進入頁面就請求全部的圖片資源,那麼可能等圖片加載出來用戶也早就走了。因此進入頁面的時候,只請求可視區域的圖片資源。
總結出來就是:
原理
圖片懶加載的原理就是暫時不設置圖片的 src 屬性,而是將圖片的 url 隱藏起來,好比先寫在 data-src 裏面,等當前圖片是否到了可視區域再將圖片真實的 url 放進 src 屬性裏面,從而實現圖片的延遲加載。
function lazyload() { let viewHeight = document.body.clientHeight //獲取可視區高度 let imgs = document.querySelectorAll('img[data-src]') imgs.forEach((item, index) => { if (item.dataset.src === '') return // 用於得到頁面中某個元素的左,上,右和下分別相對瀏覽器視窗的位置 let rect = item.getBoundingClientRect() if (rect.bottom >= 0 && rect.top < viewHeight) { item.src = item.dataset.src item.removeAttribute('data-src') } }) } // 可使用節流優化一下 window.addEventListener('scroll', lazyload)
經過上面例子的實現,咱們要實現懶加載都須要去監聽 scroll 事件,儘管咱們能夠經過函數節流的方式來阻止高頻率的執行函數,可是咱們仍是須要去計算 scrollTop,offsetHeight 等屬性,有沒有簡單的不須要計算這些屬性的方式呢,答案是有的---IntersectionObserver
const imgs = document.querySelectorAll('img[data-src]') const config = { rootMargin: '0px', threshold: 0, } let observer = new IntersectionObserver((entries, self) => { entries.forEach((entry) => { if (entry.isIntersecting) { let img = entry.target let src = img.dataset.src if (src) { img.src = src img.removeAttribute('data-src') } // 解除觀察 self.unobserve(entry.target) } }) }, config) imgs.forEach((image) => { observer.observe(image) })
圖片預加載,是指在一些須要展現大量圖片的網站,將圖片提早加載到本地緩存中,從而提高用戶體驗。
經常使用的方式有兩種,一種是隱藏在 css 的 background 的 url 屬性裏面,一種是經過 javascript 的 Image 對象設置實例對象的 src 屬性實現圖片的預加載。
一、用 CSS 和 JavaScript 實現預加載
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; } #preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; } #preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
經過 CSS 的 background 屬性將圖片預加載到屏幕外的背景上。當它們在 web 頁面的其餘地方被調用時,瀏覽器就會在渲染過程當中使用預加載(緩存)的圖片。該方法雖然高效,但仍有改進餘地。使用該法加載的圖片會同頁面的其餘內容一塊兒加載,增長了頁面的總體加載時間。
爲了解決這個問題,咱們增長了一些 JavaScript 代碼,來推遲預加載的時間,直到頁面加載完畢。
function preloader() { if (document.getElementById) { document.getElementById('preload-01').style.background = 'url(http://domain.tld/image-01.png) no-repeat -9999px -9999px' document.getElementById('preload-02').style.background = 'url(http://domain.tld/image-02.png) no-repeat -9999px -9999px' document.getElementById('preload-03').style.background = 'url(http://domain.tld/image-03.png) no-repeat -9999px -9999px' } } function addLoadEvent(func) { var oldonload = window.onload if (typeof window.onload != 'function') { window.onload = func } else { window.onload = function () { if (oldonload) { oldonload() } func() } } } addLoadEvent(preloader)
二、使用 JavaScript 實現預加載
function preloader() { if (document.images) { var img1 = new Image() var img2 = new Image() var img3 = new Image() img1.src = 'http://domain.tld/path/to/image-001.gif' img2.src = 'http://domain.tld/path/to/image-002.gif' img3.src = 'http://domain.tld/path/to/image-003.gif' } } function addLoadEvent(func) { var oldonload = window.onload if (typeof window.onload != 'function') { window.onload = func } else { window.onload = function () { if (oldonload) { oldonload() } func() } } } addLoadEvent(preloader)
什麼是響應式圖片加載?其實就是在不一樣分辨率的設備上顯示不一樣尺寸的圖片,避免資源的浪費。
經常使用的方法就是 css3 的媒體查詢(media query)。
@media screen and (min-width: 1200px) { img { background-image: url('1.png'); } } @media screen and (min-width: 992px) { img { background-image: url('2.png'); } } @media screen and (min-width: 768px) { img { background-image: url('3.png'); } } @media screen and (min-width: 480px) { img { background-image: url('4.png'); } }
此外,還可使用 HTML5 的 picture 屬性進行響應式處理。方法以下:
<picture> <source srcset="src/img/l.png" media="(min-width: 1200px)" /> <source srcset="src/img/2.png" media="(min-width: 992px)" /> <source srcset="src/img/4.png" media="(min-width: 768px)" /> <img src="src/img/4.png" /> </picture>
須要注意的是:如今不少瀏覽器對於 picture 這個標籤還不支持,使用的時候須要加以注意。
漸進式圖片的意思是在高畫質圖像加載完以前會先顯示低畫質版本。低畫質版本因爲畫質低、壓縮率高,尺寸很小,加載很快。在二者之間咱們也能夠根據須要顯示不一樣畫質的版本。
漸進式圖片可讓用戶產生圖片加載變快的印象。用戶再也不盯着一片空白區域等待圖片加載,而能看到圖像變得愈來愈清晰,這樣對用戶體驗也是友好的。
骨架屏技術也是相似的原理。