偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

淺談Web應(yīng)用中的圖片優(yōu)化技巧及反思

開發(fā) 前端
這篇文章,我們將一起探討,web應(yīng)用中能對圖片進(jìn)行什么樣的優(yōu)化,以及反思一些“負(fù)優(yōu)化”手段

這篇文章,我們將一起探討,Web應(yīng)用中能對圖片進(jìn)行什么樣的優(yōu)化,以及反思一些“負(fù)優(yōu)化”手段。

一、為什么要對圖片進(jìn)行優(yōu)化

對于大多數(shù)前端工程師來說,圖片就是UI設(shè)計師(或者自己)切好的圖,你要做的只是把圖片丟進(jìn)項目中,然后用以鏈接的方式呈現(xiàn)在頁面上,而且我們也經(jīng)常把精力放在項目的打包優(yōu)化構(gòu)建上,如何分包,如何抽取第三方庫........有時我們會忘了,圖片才是一個網(wǎng)站最大頭的那塊加載資源(見下圖),雖然圖片加載可以不不阻礙頁面渲染,但優(yōu)化圖片,絕對可以讓網(wǎng)站的體驗提升一個檔次。

二、從圖片大小開始優(yōu)化

壓縮圖片可以使用統(tǒng)一的壓縮工具 — imagemin,它是一款可以集成多個壓縮庫的工具,支持jpg,png,webp等等格式的圖片壓縮,比如pngquant,mozjpeg等等,作為測試用途,我們可以直接安裝imagemin-pngquant來嘗試png圖片的壓縮:

PNG壓縮:

  1. npm install imagemin  
  2.  npm install imagemin-pngquant 

這里先安裝imagemin庫,再安裝對應(yīng)的png壓縮庫。

  1. const imagemin = require('imagemin');  
  2.     const imageminPngquant = require('imagemin-pngquant');  
  3.     (async () => {  
  4.         await imagemin(['images/*.png'], 'build/images', {  
  5.             plugins: [  
  6.                 imageminPngquant({ quality: '65-80' })  
  7.             ]  
  8.         });  
  9.         console.log('Images optimized');  
  10.     })(); 

我們可以在quailty一項決定壓縮比率,65-80貌似是一個在壓縮率和質(zhì)量之間實現(xiàn)平衡的數(shù)值,騰訊AlloyTeam出品的gka圖片處理工具,同樣使用到了imagemin庫,他們默認(rèn)也是使用65-80的選項:

gka代碼

用它壓縮一張png圖片,我們看看效果如何:

這是壓縮前的:

這是壓縮后的:

從肉眼上幾乎看不出區(qū)別,但實際上減少了百分之77的體積!讀者可以自己保存圖片進(jìn)行比較。

JPG/JPEG壓縮與漸進(jìn)式圖片

壓縮jpg/jpeg圖片的方式與png類似,imagemin提供了兩個插件:jpegtrain和mozjpeg供我們使用。一般我們選擇mozjpeg,它擁有更豐富的壓縮選項:

  1. npm install imagemin-mozjpeg  
  1. const imagemin = require('imagemin');  
  2.     const imageminMozjpeg = require('imagemin-mozjpeg');  
  3.     (async () => {  
  4.         await imagemin(['images/*.jpg'], 'build/images', {  
  5.             use: [  
  6.                 imageminMozjpeg({ quality: 65, progressive: true })  
  7.             ]  
  8.         });  
  9.         console.log('Images optimized');  
  10.     })(); 

注意到我們使用了progressive: true選項,這可以將圖片轉(zhuǎn)換為漸進(jìn)式圖片,關(guān)于漸進(jìn)式圖片,它允許在加載照片的時候,如果網(wǎng)速比較慢的話,先顯示一個類似模糊有點小馬賽克的質(zhì)量比較差的照片,然后慢慢的變?yōu)榍逦恼掌?/p>

而相比之下,非漸進(jìn)式的圖片(Baseline JPEG)則會老老實實地從頭到尾去加載:

張鑫旭大神的這篇文章,可以幫你更好地了解兩者的區(qū)別:

漸進(jìn)式j(luò)peg(progressive jpeg)圖片及其相關(guān)

簡單來說,漸進(jìn)式圖片一開始就決定了大小,而不像Baseline圖片一樣,不斷地從上往下加載,從而造成多次回流,但漸進(jìn)式圖片需要消耗CPU去多次計算渲染,這是其主要缺點。

當(dāng)然,交錯式png也可以實現(xiàn)相應(yīng)的效果,但目前pngquant沒有實現(xiàn)轉(zhuǎn)換功能,但是ps中導(dǎo)出png時是可以設(shè)置為交錯式的。

在真實項目中如何操作?

實際項目中,總不能UI丟一個圖過來你就跑一遍壓縮代碼吧?幸好imagemin有對應(yīng)的webpack插件,在webpack遍地使用的今天,我們可以輕松實現(xiàn)批量壓縮: 

  1. npm install imagemin-webpack-plugin 

先安裝imagemin-webpack-plugin 

  1. import ImageminPlugin from 'imagemin-webpack-plugin'  
  2.   import imageminMozjpeg from 'imagemin-mozjpeg'  
  3.   module.exports = {  
  4.     plugins: [  
  5.       new ImageminPlugin({  
  6.         plugins: [  
  7.           imageminMozjpeg({  
  8.             quality: 100,  
  9.             progressive: true  
  10.           })  
  11.         ]  
  12.       })  
  13.     ]  
  14.   } 

接著在webpack配置文件中,引入自己需要的插件,使用方法完全相同。具體可參考github的文檔imagemin-webpack-plugin。

三、通過圖片按需加載減少請求壓力

圖片按需加載是個老生常談的話題,傳統(tǒng)做法自然是通過監(jiān)聽頁面滾動位置,符合條件了再去進(jìn)行資源加載,我們看看如今還有什么方法可以做到按需加載。

使用強(qiáng)大的IntersectionObserver

IntersectionObserver提供給我們一項能力:可以用來監(jiān)聽元素是否進(jìn)入了設(shè)備的可視區(qū)域之內(nèi),這意味著:我們等待圖片元素進(jìn)入可視區(qū)域后,再決定是否加載它,畢竟用戶沒看到圖片前,根本不關(guān)心它是否已經(jīng)加載了。

這是Chrome51率先提出和支持的API,而在2019年的今天,各大瀏覽器對它的支持度已經(jīng)有所改善(除了IE,全線崩~):

廢話不多說,上代碼:

首先,假設(shè)我們有一個圖片列表,它們的src屬性我們暫不設(shè)置,而用data-src來替代: 

  1. <li>  
  2.   <img class="list-item-img" alt="loading" data-src='a.jpg'/>  
  3. </li>  
  4. <li>  
  5.   <img class="list-item-img" alt="loading" data-src='b.jpg'/>  
  6. </li>  
  7. <li>  
  8.   <img class="list-item-img" alt="loading" data-src='c.jpg'/>  
  9. </li>  
  10. <li>  
  11.   <img class="list-item-img" alt="loading" data-src='d.jpg'/>  
  12. </li> 

這樣會導(dǎo)致圖片無法加載,這當(dāng)然不是我們的目的,我們想做的是,當(dāng)IntersectionObserver監(jiān)聽到圖片元素進(jìn)入可視區(qū)域時,將data-src"還給"src屬性,這樣我們就可以實現(xiàn)圖片加載了:

  1. const observer = new IntersectionObserver(function(changes) {  
  2.   changes.forEach(function(element, index) {  
  3.    // 當(dāng)這個值大于0,說明滿足我們的加載條件了,這個值可通過rootMargin手動設(shè)置  
  4.     if (element.intersectionRatio > 0) {  
  5.       // 放棄監(jiān)聽,防止性能浪費(fèi),并加載圖片。  
  6.       observer.unobserve(element.target);  
  7.       elementelement.target.src = element.target.dataset.src;  
  8.     }  
  9.   });  
  10. });  
  11. function initObserver() {  
  12.   const listItems = document.querySelectorAll('.list-item-img');  
  13.   listItems.forEach(function(item) {  
  14.    // 對每個list元素進(jìn)行監(jiān)聽  
  15.     observer.observe(item);  
  16.   });  
  17.  
  18. initObserver(); 

運(yùn)行代碼并觀察控制臺的Network,會發(fā)現(xiàn)圖片隨著可視區(qū)域的移動而加載,我們的目的達(dá)到了。

這里給出一個線上demo,供大家調(diào)試學(xué)習(xí)

(ps: 這里額外介紹一個vue的圖片懶加載組件vue-view-lazy,也是基于IntersectionObserver實現(xiàn)的)。

還是Chrome的黑科技——loading屬性

從新版本Chrome(76)開始,已經(jīng)默認(rèn)支持一種新的html屬性——loading,它包含三種取值:auto、lazy和eager(ps: 之前有文章說是lazyload屬性,后來chrome的工程師已經(jīng)將其確定為loading屬性,原因是lazyload語義不夠明確),我們看看這三種屬性有什么不同:

  • auto:讓瀏覽器自動決定是否進(jìn)行懶加載,這其中的機(jī)制尚不明確。
  • lazy:明確地讓瀏覽器對此圖片進(jìn)行懶加載,即當(dāng)用戶滾動到圖片附近時才進(jìn)行加載,但目前沒有具體說明這個“附近”具體是多近。
  • eager:讓瀏覽器立刻加載此圖片,也不是此篇文章關(guān)注的功能。

我們可以通過chrome的開發(fā)工具看看這個demo 中的圖片加載方式,我們把上一個demo中的js腳本都刪掉了,只用了loading=lazy這個屬性。接著,勾選工具欄中的Disabled Cache后仔細(xì)觀察Network一欄,細(xì)心的人應(yīng)該會發(fā)現(xiàn),一張圖片被分為了兩次去請求!第一次的狀態(tài)碼是206,第二次的狀態(tài)碼才是200,如圖所示:

這個現(xiàn)象跟chrome的lazy-loading功能的實現(xiàn)機(jī)制有關(guān):

首先,瀏覽器會發(fā)送一個預(yù)請求,請求地址就是這張圖片的url,但是這個請求只拉取這張圖片的頭部數(shù)據(jù),大約2kb,具體做法是在請求頭中設(shè)置range: bytes=0-2047,

而從這段數(shù)據(jù)中,瀏覽器就可以解析出圖片的寬高等基本維度,接著瀏覽器立馬為它生成一個空白的占位,以免圖片加載過程中頁面不斷跳動,這很合理,總不能為了一個懶加載,讓用戶犧牲其他方面的體驗吧?這個請求返回的狀態(tài)碼是206,表明:客戶端通過發(fā)送范圍請求頭Range抓取到了資源的部分?jǐn)?shù)據(jù),詳細(xì)的狀態(tài)碼解釋可以看看這篇文章。

然后,在用戶滾動到圖片附近時,再發(fā)起一個請求,完整地拉取圖片的數(shù)據(jù)下來,這個才是我們熟悉的狀態(tài)碼200請求。

可以預(yù)測到,如果以后這個屬性被普遍使用,那一個服務(wù)器要處理的圖片請求連接數(shù)可能會變成兩倍,對服務(wù)器的壓力會有所增大,但時代在進(jìn)步,我們可以依靠http2多路復(fù)用的特性來緩解這個壓力,這時候就需要技術(shù)負(fù)責(zé)人權(quán)衡利弊了。

要注意,使用這項特性進(jìn)行圖片懶加載時,記得先進(jìn)行兼容性處理,對不支持這項屬性的瀏覽器,轉(zhuǎn)而使用JavaScript來實現(xiàn),比如上面說到的IntersectionObserver: 

  1. if ("loading" in HTMLImageElement.prototype) {  
  2.     // 沒毛病  
  3.   } else {  
  4.     // .....  
  5.   } 

還可以做到錦上添花!

以上介紹的兩種方式,其實最終實現(xiàn)的效果是相似的,但這里還有個問題,當(dāng)網(wǎng)速慢的時候,圖片還沒加載完之前,用戶會看到一段空白的時間,在這段空白時間,就算是漸進(jìn)式圖片也無法發(fā)揮它的作用,我們需要更友好的展示方式來彌補(bǔ)這段空白,有一種方法簡單粗暴,那就是用一張占位圖來頂替,這張占位圖被加載過一次后,即可從緩存中取出,無須重新加載,但這種圖片會顯得有些千篇一律,并不能很好地做到preview的效果。

這里我向大家介紹另一種占位圖做法——css漸變色背景,原理很簡單,當(dāng)img標(biāo)簽的圖片還沒加載出來,我們可以為其設(shè)置背景色,比如: 

  1. <img src="a.jpg" style="background: red;"/> 

這樣會先顯示出紅色背景,再渲染出真實的圖片,重點來了,我們此時要借用工具為這張圖片"配制"出合適的漸變背景色,以達(dá)到部分preview的效果,我們可以使用https://calendar.perfplanet.com/2018/gradient-image-placeholders/ 這篇文章中推薦的工具GIP進(jìn)行轉(zhuǎn)換,這里附上在線轉(zhuǎn)換的地址https://tools.w3clubs.com/gip/

經(jīng)過轉(zhuǎn)換后,我們得到了下面這串代碼:   

  1. background: linear-gradient(  
  2.       to bottom,  
  3.       #1896f5 0%,  
  4.       #2e6d14 100%  
  5.     ) 

最終效果如下所示:

[[273773]]

四、響應(yīng)式圖片的實踐

我們經(jīng)常會遇到這種情況:一張在普通筆記本上顯示清晰的圖片,到了蘋果的Retina屏幕或是其他高清晰度的屏幕上,就變得模糊了。

這是因為,在同樣尺寸的屏幕上,高清屏可以展示的物理像素點比普通屏多,比如Retina屏,同樣的屏幕尺寸下,它的物理像素點的個數(shù)是普通屏的4倍(2 * 2),所以普通屏上顯示清晰的圖片,在高清屏上就像是被放大了,自然就變得模糊了,要從圖片資源上解決這個問題,就需要在設(shè)備像素密度為2的高清屏中,對應(yīng)地展示一張兩倍大小的圖。

而通常來講,對于背景圖片,我們可以使用css的@media進(jìn)行媒體查詢,以決定不同像素密度下該用哪張倍圖,例如:   

  1. .bg {  
  2.        background-image: url("bg.png");  
  3.        width: 100px;  
  4.        height: 100px;  
  5.        background-size: 100% 100%;  
  6.    }  
  7.    @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)  
  8.    {  
  9.        .bg {  
  10.            background-image: url("bg@2x.png") // 尺寸為200 * 200的圖  
  11.        }  
  12.    } 

這么做有兩個好處,一是保證高像素密度的設(shè)備下,圖片仍能保持應(yīng)有的清晰度,二是防止在低像素密度的設(shè)備下加載大尺寸圖片造成浪費(fèi)。

那么如何處理img標(biāo)簽?zāi)兀?/p>

我們可以使用HTML5中img標(biāo)簽的srcset來達(dá)到這個效果,看看下面這段代碼: 

  1. <img width="320"  src="bg@2x.png" srcset="bg.png 1x;bg@2x.png 2x"/> 

這段代碼的作用是:當(dāng)設(shè)備像素密度,也就是dpr(devicePixelRatio)為1時,使用bg.png,為2時使用二倍圖bg@2x.png,依此類推,你可以根據(jù)需要設(shè)置多種精度下要加載的圖片,如果沒有命中,瀏覽器會選擇最鄰近的一個精度對應(yīng)的圖片進(jìn)行加載。

要注意:老舊的瀏覽器不支持srcset的特性,它會繼續(xù)正常加載src屬性引用的圖像。

五、安全地使用WebP圖片

WebP的優(yōu)勢這里不再贅述,簡單來說就是:同樣尺寸的圖片,WebP能保證比未壓縮過的png、jpg、gif等格式的圖片減少百分之40-70(甚至90)的比例,且保證較高的質(zhì)量,更可以支持顯示動態(tài)圖和透明通道。

但目前WebP的兼容性并不太好:

但我們可以通過兩種方式,對暫未支持webp的瀏覽器進(jìn)行兼容:

picture結(jié)合source標(biāo)簽

HTML5的picture標(biāo)簽,可以理解為相框,里面可以支持多種格式的圖片,并保留一張默認(rèn)底圖:   

  1. <picture>  
  2.      <source srcset="bg.webp" type="image/webp">  
  3.      <source srcset="bg.jpg" type="image/jpeg">   
  4.      <img src="bg.jpg" alt="背景圖">  
  5.    </picture> 

有了這段代碼,瀏覽器會自動根據(jù)是否支持webp格式來選擇加載哪張圖片,若不支持,則會顯示bg.jpg,如果瀏覽器連picture都不支持,那么會fallback到默認(rèn)的img圖片,這是必不可少的一個選項。

而且這里要注意source的放置順序,如果把jpg放在第一位,webp放在第二位,即使瀏覽器支持webp,那也會選擇加載jpg圖片。

借助cdn服務(wù)自動判斷

目前,有些圖片cdn服務(wù)可以開啟自動兼容webp的模式,即支持webp的瀏覽器則將原圖轉(zhuǎn)換為webp圖片并返回,否則直接返回原圖。實現(xiàn)這個功能的原理是,根據(jù)瀏覽器發(fā)起的請求頭中的Accept屬性中是否包含webp格式來判斷:

有則說明瀏覽器支持webp格式,這對開發(fā)者來說可能是最簡單的兼容方案,但是依賴于后端服務(wù)。

接下來,談一談我認(rèn)為應(yīng)該反思的負(fù)優(yōu)化手段:

七、對Base64Url的反思

首先復(fù)習(xí)一下Base64的概念,Base64就是一種基于64個可打印字符來表示二進(jìn)制數(shù)據(jù)的方法,編碼過程是從二進(jìn)制數(shù)據(jù)到字符串的過程,在web應(yīng)用中我們經(jīng)常用它來做啥呢——傳輸圖片數(shù)據(jù)。HTML中,img的src和css樣式的background-image都可以接受base64字符串,從而在頁面上渲染出對應(yīng)的圖片。正是基于瀏覽器的這項能力,很多開發(fā)者提出了將多張圖片轉(zhuǎn)換為base64字符串,放進(jìn)css樣式文件中的“優(yōu)化方式”,這樣做的目的只有一個——減少HTTP請求數(shù)。但實際上,在如今的應(yīng)用開發(fā)中,這種做法大多數(shù)情況是“負(fù)優(yōu)化”效果,接下來讓我們細(xì)數(shù)base64 Url的“罪狀”:

第一、讓css文件的體積失去控制

當(dāng)你把圖片轉(zhuǎn)換為base64字符串之后,字符串的體積一般會比原圖更大,一般會多出接近3成的大小,如果你一個頁面中有20張平均大小為50kb的圖片,轉(zhuǎn)它們?yōu)閎ase64后,你的css文件將可能增大1.2mb的大小,這樣將嚴(yán)重阻礙瀏覽器的關(guān)鍵渲染路徑:

css文件本身就是渲染阻塞資源,瀏覽器首次加載時如果沒有全部下載和解析完css內(nèi)容就無法進(jìn)行渲染樹的構(gòu)建,而base64的嵌入則是雪上加霜,這將把原先瀏覽器可以進(jìn)行優(yōu)化的圖片異步加載,變成首屏渲染的阻塞和延遲。

或許有人會說,webpack的url-loader可以根據(jù)圖片大小決定是否轉(zhuǎn)為base64(一般是小于10kb的圖片),但你也應(yīng)該擔(dān)心如果頁面中有100張小于10kb的圖片時,會給css文件增加多少體積。

第二、讓瀏覽器的資源緩存策略功虧一簣

假設(shè)你的base64Url會被你的應(yīng)用多次復(fù)用,本來瀏覽器可以直接從本地緩存取出的圖片,換成base64Url,將造成應(yīng)用中多個頁面重復(fù)下載1.3倍大小的文本,假設(shè)一張圖片是100kb大小,被你的應(yīng)用使用了10次,那么造成的流量浪費(fèi)將是:(100 1.3 10) - 100 = 1200kb。

第三、低版本瀏覽器的兼容問題

這是比較次要的問題,dataurl在低版本IE瀏覽器,比如IE8及以下的瀏覽器,會有兼容性問題,詳細(xì)情況可以參考這篇文章。

第四、不利于開發(fā)者工具調(diào)試與查看

無論哪張圖片,看上去都是一堆沒有意義的字符串,光看代碼無法知道原圖是哪張,不利于某些情況下的比對。

說了這么多,有人可能不服氣,既然這種方案缺點這么多,為啥它會從以前就被廣泛使用呢?這要從早期的http協(xié)議特性說起,在http1.1之前,http協(xié)議尚未實現(xiàn)keep-alive,也就是每一次請求,都必須走三次握手四次揮手去建立連接,連接完又丟棄無法復(fù)用,而即使是到了http1.1的時代,keep-alive可以保證tcp的長連接,不需要多次重新建立,但由于http1.1是基于文本分割的協(xié)議,所以消息是串行的,必須有序地逐個解析,所以在這種請求“昂貴”,且早期圖片體積并不是特別大,用戶對網(wǎng)頁的響應(yīng)速度和體驗要求也不是很高的各種前提結(jié)合下,減少圖片資源的請求數(shù)是可以理解的。

但是,在越來越多網(wǎng)站支持http2.0的前提下,這些都不是問題,h2是基于二進(jìn)制幀的協(xié)議,在保留http1.1長連接的前提下,實現(xiàn)了消息的并行處理,請求和響應(yīng)可以交錯甚至可以復(fù)用,多個并行請求的開銷已經(jīng)大大降低,我已經(jīng)不知道還有什么理由繼續(xù)堅持base64Url的使用了。

總結(jié)

圖片優(yōu)化的手段總是隨著瀏覽器特性的升級,網(wǎng)絡(luò)傳輸協(xié)議的升級,以及用戶對體驗要求的提升而不停地更新迭代,幾年前適用的或顯著的優(yōu)化手段,幾年后不一定仍然如此。因地制宜,多管齊下,才能將其優(yōu)化做到極致! 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2011-05-18 16:02:08

XML

2022-11-29 19:44:47

WebOpenResty防火墻

2012-09-29 13:33:16

Squid圖片存儲存儲架構(gòu)

2012-06-26 10:35:40

Squid架構(gòu)

2014-12-10 10:12:02

Web

2011-09-08 17:48:33

Web Widget

2009-07-05 11:23:44

2019-03-13 09:00:00

Web應(yīng)用SPAJavaScript

2024-07-29 00:00:05

2023-12-07 19:19:11

2010-09-29 16:38:03

企業(yè)應(yīng)用訪問

2023-12-17 14:36:05

2019-01-23 17:08:03

開發(fā)

2009-01-27 20:36:00

2019-07-16 11:15:04

JavaScriptCSS數(shù)據(jù)庫

2022-05-11 12:15:50

scriptweb性能

2023-01-26 01:33:09

web性能優(yōu)化

2022-01-07 06:09:23

Web性能優(yōu)化

2019-12-23 10:20:12

Web圖片優(yōu)化前端

2009-12-29 16:08:41

Silverlight
點贊
收藏

51CTO技術(shù)棧公眾號