摘要:要注意老舊的瀏覽器不支持的特性,它會繼續正常加載屬性引用的圖像。五安全地使用圖片的優勢這里不再贅述,簡單來說
這篇文章,我們將一起探討,web應用中能對圖片進行什么樣的優化,以及反思一些“負優化”手段
一、為什么要對圖片進行優化對于大多數前端工程師來說,圖片就是UI設計師(或者自己)切好的圖,你要做的只是把圖片丟進項目中,然后用以鏈接的方式呈現在頁面上,而且我們也經常把精力放在項目的打包優化構建上,如何分包,如何抽取第三方庫........有時我們會忘了,圖片才是一個網站最大頭的那塊加載資源(見下圖),雖然圖片加載可以不不阻礙頁面渲染,但優化圖片,絕對可以讓網站的體驗提升一個檔次。
二、從圖片大小開始優化壓縮圖片可以使用統一的壓縮工具 — imagemin,它是一款可以集成多個壓縮庫的工具,支持jpg,png,webp等等格式的圖片壓縮,比如pngquant,mozjpeg等等,作為測試用途,我們可以直接安裝imagemin-pngquant來嘗試png圖片的壓縮:
PNG壓縮npm install imagemin npm install imagemin-pngquant
這里先安裝imagemin庫,再安裝對應的png壓縮庫
const imagemin = require("imagemin"); const imageminPngquant = require("imagemin-pngquant"); (async () => { await imagemin(["images/*.png"], "build/images", { plugins: [ imageminPngquant({ quality: "65-80" }) ] }); console.log("Images optimized"); })();
我們可以在quailty一項決定壓縮比率,65-80貌似是一個在壓縮率和質量之間實現平衡的數值,騰訊AlloyTeam出品的gka圖片處理工具,同樣使用到了imagemin庫,他們默認也是使用65-80的選項:
gka代碼
用它壓縮一張png圖片,我們看看效果如何:
這是壓縮前的:
這是壓縮后的:
從肉眼上幾乎看不出區別,但實際上減少了百分之77的體積!讀者可以自己保存圖片進行比較。
JPG/JPEG壓縮與漸進式圖片壓縮jpg/jpeg圖片的方式與png類似,imagemin提供了兩個插件:jpegtrain和mozjpeg供我們使用。一般我們選擇mozjpeg,它擁有更豐富的壓縮選項:
npm install imagemin-mozjpeg
const imagemin = require("imagemin"); const imageminMozjpeg = require("imagemin-mozjpeg"); (async () => { await imagemin(["images/*.jpg"], "build/images", { use: [ imageminMozjpeg({ quality: 65, progressive: true }) ] }); console.log("Images optimized"); })();
注意到我們使用了progressive: true選項,這可以將圖片轉換為漸進式圖片,關于漸進式圖片,它允許在加載照片的時候,如果網速比較慢的話,先顯示一個類似模糊有點小馬賽克的質量比較差的照片,然后慢慢的變為清晰的照片:
而相比之下,非漸進式的圖片(Baseline JPEG)則會老老實實地從頭到尾去加載:
張鑫旭大神的這篇文章,可以幫你更好地了解兩者的區別:
漸進式jpeg(progressive jpeg)圖片及其相關
簡單來說,漸進式圖片一開始就決定了大小,而不像Baseline圖片一樣,不斷地從上往下加載,從而造成多次回流,但漸進式圖片需要消耗CPU去多次計算渲染,這是其主要缺點。
當然,交錯式png也可以實現相應的效果,但目前pngquant沒有實現轉換功能,但是ps中導出png時是可以設置為交錯式的。
實際項目中,總不能UI丟一個圖過來你就跑一遍壓縮代碼吧?幸好imagemin有對應的webpack插件,在webpack遍地使用的今天,我們可以輕松實現批量壓縮:
npm install imagemin-webpack-plugin
先安裝imagemin-webpack-plugin
import ImageminPlugin from "imagemin-webpack-plugin" import imageminMozjpeg from "imagemin-mozjpeg" module.exports = { plugins: [ new ImageminPlugin({ plugins: [ imageminMozjpeg({ quality: 100, progressive: true }) ] }) ] }
接著在webpack配置文件中,引入自己需要的插件,使用方法完全相同。具體可參考github的文檔imagemin-webpack-plugin
三、通過圖片按需加載減少請求壓力圖片按需加載是個老生常談的話題,傳統做法自然是通過監聽頁面滾動位置,符合條件了再去進行資源加載,我們看看如今還有什么方法可以做到按需加載。
使用強大的IntersectionObserverIntersectionObserver提供給我們一項能力:可以用來監聽元素是否進入了設備的可視區域之內,這意味著:我們等待圖片元素進入可視區域后,再決定是否加載它,畢竟用戶沒看到圖片前,根本不關心它是否已經加載了。
這是Chrome51率先提出和支持的API,而在2019年的今天,各大瀏覽器對它的支持度已經有所改善(除了IE,全線崩~):
廢話不多說,上代碼:
首先,假設我們有一個圖片列表,它們的src屬性我們暫不設置,而用data-src來替代:
這樣會導致圖片無法加載,這當然不是我們的目的,我們想做的是,當IntersectionObserver監聽到圖片元素進入可視區域時,將data-src"還給"src屬性,這樣我們就可以實現圖片加載了:
const observer = new IntersectionObserver(function(changes) { changes.forEach(function(element, index) { // 當這個值大于0,說明滿足我們的加載條件了,這個值可通過rootMargin手動設置 if (element.intersectionRatio > 0) { // 放棄監聽,防止性能浪費,并加載圖片。 observer.unobserve(element.target); element.target.src = element.target.dataset.src; } }); }); function initObserver() { const listItems = document.querySelectorAll(".list-item-img"); listItems.forEach(function(item) { // 對每個list元素進行監聽 observer.observe(item); }); } initObserver();
運行代碼并觀察控制臺的Network,會發現圖片隨著可視區域的移動而加載,我們的目的達到了。
這里給出一個線上demo,供大家調試學習
(ps: 這里額外介紹一個vue的圖片懶加載組件vue-view-lazy,也是基于IntersectionObserver實現的)。
從新版本Chrome(76)開始,已經默認支持一種新的html屬性——loading,它包含三種取值:auto、lazy和eager(ps: 之前有文章說是lazyload屬性,后來chrome的工程師已經將其確定為loading屬性,原因是lazyload語義不夠明確),我們看看這三種屬性有什么不同:
auto:讓瀏覽器自動決定是否進行懶加載,這其中的機制尚不明確。
lazy:明確地讓瀏覽器對此圖片進行懶加載,即當用戶滾動到圖片附近時才進行加載,但目前沒有具體說明這個“附近”具體是多近。
eager:讓瀏覽器立刻加載此圖片,也不是此篇文章關注的功能。
我們可以通過chrome的開發工具看看這個demo中的圖片加載方式,我們把上一個demo中的js腳本都刪掉了,只用了loading=lazy這個屬性。接著,勾選工具欄中的Disabled Cache后仔細觀察Network一欄,細心的人應該會發現,一張圖片被分為了兩次去請求!第一次的狀態碼是206,第二次的狀態碼才是200,如圖所示:
這個現象跟chrome的lazy-loading功能的實現機制有關:
首先,瀏覽器會發送一個預請求,請求地址就是這張圖片的url,但是這個請求只拉取這張圖片的頭部數據,大約2kb,具體做法是在請求頭中設置range: bytes=0-2047,
而從這段數據中,瀏覽器就可以解析出圖片的寬高等基本維度,接著瀏覽器立馬為它生成一個空白的占位,以免圖片加載過程中頁面不斷跳動,這很合理,總不能為了一個懶加載,讓用戶犧牲其他方面的體驗吧?這個請求返回的狀態碼是206,表明:客戶端通過發送范圍請求頭Range抓取到了資源的部分數據,詳細的狀態碼解釋可以看看這篇文章
然后,在用戶滾動到圖片附近時,再發起一個請求,完整地拉取圖片的數據下來,這個才是我們熟悉的狀態碼200請求。
可以預測到,如果以后這個屬性被普遍使用,那一個服務器要處理的圖片請求連接數可能會變成兩倍,對服務器的壓力會有所增大,但時代在進步,我們可以依靠http2多路復用的特性來緩解這個壓力,這時候就需要技術負責人權衡利弊了
要注意,使用這項特性進行圖片懶加載時,記得先進行兼容性處理,對不支持這項屬性的瀏覽器,轉而使用JavaScript來實現,比如上面說到的IntersectionObserver:
if ("loading" in HTMLImageElement.prototype) { // 沒毛病 } else { // ..... }還可以做到錦上添花!
以上介紹的兩種方式,其實最終實現的效果是相似的,但這里還有個問題,當網速慢的時候,圖片還沒加載完之前,用戶會看到一段空白的時間,在這段空白時間,就算是漸進式圖片也無法發揮它的作用,我們需要更友好的展示方式來彌補這段空白,有一種方法簡單粗暴,那就是用一張占位圖來頂替,這張占位圖被加載過一次后,即可從緩存中取出,無須重新加載,但這種圖片會顯得有些千篇一律,并不能很好地做到preview的效果。
這里我向大家介紹另一種占位圖做法——css漸變色背景,原理很簡單,當img標簽的圖片還沒加載出來,我們可以為其設置背景色,比如:
這樣會先顯示出紅色背景,再渲染出真實的圖片,重點來了,我們此時要借用工具為這張圖片"配制"出合適的漸變背景色,以達到部分preview的效果,我們可以使用https://calendar.perfplanet.com/2018/gradient-image-placeholders/ 這篇文章中推薦的工具GIP進行轉換,這里附上在線轉換的地址https://tools.w3clubs.com/gip/
經過轉換后,我們得到了下面這串代碼:
background: linear-gradient( to bottom, #1896f5 0%, #2e6d14 100% )
最終效果如下所示:
我們經常會遇到這種情況:一張在普通筆記本上顯示清晰的圖片,到了蘋果的Retina屏幕或是其他高清晰度的屏幕上,就變得模糊了。
這是因為,在同樣尺寸的屏幕上,高清屏可以展示的物理像素點比普通屏多,比如Retina屏,同樣的屏幕尺寸下,它的物理像素點的個數是普通屏的4倍(2 * 2),所以普通屏上顯示清晰的圖片,在高清屏上就像是被放大了,自然就變得模糊了,要從圖片資源上解決這個問題,就需要在設備像素密度為2的高清屏中,對應地展示一張兩倍大小的圖。
而通常來講,對于背景圖片,我們可以使用css的@media進行媒體查詢,以決定不同像素密度下該用哪張倍圖,例如:
.bg { background-image: url("bg.png"); width: 100px; height: 100px; background-size: 100% 100%; } @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2) { .bg { background-image: url("bg@2x.png") // 尺寸為200 * 200的圖 } }
這么做有兩個好處,一是保證高像素密度的設備下,圖片仍能保持應有的清晰度,二是防止在低像素密度的設備下加載大尺寸圖片造成浪費。
那么如何處理img標簽呢?
我們可以使用HTML5中img標簽的srcset來達到這個效果,看看下面這段代碼:
這段代碼的作用是:當設備像素密度,也就是dpr(devicePixelRatio)為1時,使用bg.png,為2時使用二倍圖bg@2x.png,依此類推,你可以根據需要設置多種精度下要加載的圖片,如果沒有命中,瀏覽器會選擇最鄰近的一個精度對應的圖片進行加載。
要注意:老舊的瀏覽器不支持srcset的特性,它會繼續正常加載src屬性引用的圖像。
WebP的優勢這里不再贅述,簡單來說就是:同樣尺寸的圖片,WebP能保證比未壓縮過的png、jpg、gif等格式的圖片減少百分之40-70(甚至90)的比例,且保證較高的質量,更可以支持顯示動態圖和透明通道。
但目前WebP的兼容性并不太好:
但我們可以通過兩種方式,對暫未支持webp的瀏覽器進行兼容:
HTML5的picture標簽,可以理解為相框,里面可以支持多種格式的圖片,并保留一張默認底圖:
有了這段代碼,瀏覽器會自動根據是否支持webp格式來選擇加載哪張圖片,若不支持,則會顯示bg.jpg,如果瀏覽器連picture都不支持,那么會fallback到默認的img圖片,這是必不可少的一個選項。
而且這里要注意source的放置順序,如果把jpg放在第一位,webp放在第二位,即使瀏覽器支持webp,那也會選擇加載jpg圖片。
目前,有些圖片cdn服務可以開啟自動兼容webp的模式,即支持webp的瀏覽器則將原圖轉換為webp圖片并返回,否則直接返回原圖。實現這個功能的原理是,根據瀏覽器發起的請求頭中的Accept屬性中是否包含webp格式來判斷:
有則說明瀏覽器支持webp格式,這對開發者來說可能是最簡單的兼容方案,但是依賴于后端服務。
接下來,談一談我認為應該反思的負優化手段:
六、對Base64Url的反思首先復習一下Base64的概念,Base64就是一種基于64個可打印字符來表示二進制數據的方法,編碼過程是從二進制數據到字符串的過程,在web應用中我們經常用它來做啥呢——傳輸圖片數據。HTML中,img的src和css樣式的background-image都可以接受base64字符串,從而在頁面上渲染出對應的圖片。正是基于瀏覽器的這項能力,很多開發者提出了將多張圖片轉換為base64字符串,放進css樣式文件中的“優化方式”,這樣做的目的只有一個——減少HTTP請求數。但實際上,在如今的應用開發中,這種做法大多數情況是“負優化”效果,接下來讓我們細數base64 Url的“罪狀”:
第一、讓css文件的體積失去控制當你把圖片轉換為base64字符串之后,字符串的體積一般會比原圖更大,一般會多出接近3成的大小,如果你一個頁面中有20張平均大小為50kb的圖片,轉它們為base64后,你的css文件將可能增大1.2mb的大小,這樣將嚴重阻礙瀏覽器的關鍵渲染路徑:
css文件本身就是渲染阻塞資源,瀏覽器首次加載時如果沒有全部下載和解析完css內容就無法進行渲染樹的構建,而base64的嵌入則是雪上加霜,這將把原先瀏覽器可以進行優化的圖片異步加載,變成首屏渲染的阻塞和延遲。
或許有人會說,webpack的url-loader可以根據圖片大小決定是否轉為base64(一般是小于10kb的圖片),但你也應該擔心如果頁面中有100張小于10kb的圖片時,會給css文件增加多少體積。
第二、讓瀏覽器的資源緩存策略功虧一簣假設你的base64Url會被你的應用多次復用,本來瀏覽器可以直接從本地緩存取出的圖片,換成base64Url,將造成應用中多個頁面重復下載1.3倍大小的文本,假設一張圖片是100kb大小,被你的應用使用了10次,那么造成的流量浪費將是:(100 1.3 10) - 100 = 1200kb。
第三、低版本瀏覽器的兼容問題這是比較次要的問題,dataurl在低版本IE瀏覽器,比如IE8及以下的瀏覽器,會有兼容性問題,詳細情況可以參考這篇文章。
第四、不利于開發者工具調試與查看無論哪張圖片,看上去都是一堆沒有意義的字符串,光看代碼無法知道原圖是哪張,不利于某些情況下的比對。
說了這么多,有人可能不服氣,既然這種方案缺點這么多,為啥它會從以前就被廣泛使用呢?這要從早期的http協議特性說起,在http1.1之前,http協議尚未實現keep-alive,也就是每一次請求,都必須走三次握手四次揮手去建立連接,連接完又丟棄無法復用,而即使是到了http1.1的時代,keep-alive可以保證tcp的長連接,不需要多次重新建立,但由于http1.1是基于文本分割的協議,所以消息是串行的,必須有序地逐個解析,所以在這種請求“昂貴”,且早期圖片體積并不是特別大,用戶對網頁的響應速度和體驗要求也不是很高的各種前提結合下,減少圖片資源的請求數是可以理解的。
但是,在越來越多網站支持http2.0的前提下,這些都不是問題,h2是基于二進制幀的協議,在保留http1.1長連接的前提下,實現了消息的并行處理,請求和響應可以交錯甚至可以復用,多個并行請求的開銷已經大大降低,我已經不知道還有什么理由繼續堅持base64Url的使用了。
總結圖片優化的手段總是隨著瀏覽器特性的升級,網絡傳輸協議的升級,以及用戶對體驗要求的提升而不停地更新迭代,幾年前適用的或顯著的優化手段,幾年后不一定仍然如此。因地制宜,多管齊下,才能將其優化做到極致!
ps:歡迎關注微信公眾號——前端漫游指南,會定期發布優質原創文章和譯文,關注公眾號福利:回復666可以獲得精選前端進階電子書,感謝~文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106671.html
摘要:網易跨境電商考拉海購在線筆試現場技術面面。如何看待校招面試招聘,對公司而言,是尋找勞動力對員工而言,是尋找未來的同事。 如何準備校招技術面試 標簽 : 面試 [TOC] 2017 年互聯網校招已近尾聲,作為一個非 CS 專業的應屆生,零 ACM 經驗、零期刊論文發表,我通過自己的努力和準備,從找實習到校招一路運氣不錯,面試全部通過,謹以此文記錄我的校招感悟。 寫在前面 寫作動機 ...
摘要:歡迎來我的個人站點性能優化其他優化瀏覽器關鍵渲染路徑開啟性能優化之旅高性能滾動及頁面渲染優化理論寫法對壓縮率的影響唯快不破應用的個優化步驟進階鵝廠大神用直出實現網頁瞬開緩存網頁性能管理詳解寫給后端程序員的緩存原理介紹年底補課緩存機制優化動 歡迎來我的個人站點 性能優化 其他 優化瀏覽器關鍵渲染路徑 - 開啟性能優化之旅 高性能滾動 scroll 及頁面渲染優化 理論 | HTML寫法...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
閱讀 3419·2021-11-15 11:39
閱讀 1552·2021-09-22 10:02
閱讀 1308·2021-08-27 16:24
閱讀 3596·2019-08-30 15:52
閱讀 3412·2019-08-29 16:20
閱讀 823·2019-08-28 18:12
閱讀 549·2019-08-26 18:27
閱讀 714·2019-08-26 13:32