圖片一般是移動端網頁中最佔流量的資源。在不少類型的網站中(好比電商、社區),圖片佔據了網頁的大部分空間。優化圖片的質量和加載速度成爲提升用戶體驗的重要途徑。javascript
目前有一些被普遍應用的方案,例如選擇壓縮比更高的圖片格式、使用雪碧圖、HTTP二、CDN等。5年前的一條知乎回答前端開發中,對圖片的優化技巧有哪些? - 賀師俊的回答 - 知乎如今依然有效,這些方案在各大網站中仍然發揮着重要的做用。html
隨着移動端興起,各類移動設備帶來了豐富的機型尺寸和像素密度,逐漸暴露出以上方案共同的問題,即都是基於網站粒度的通用方案。咱們沒法保證在每種機型上都達到最優的效果,只能經過一些取捨來達到較均衡的方案,從而知足大部分用戶的體驗需求。(雖然經過Media Query等方式能夠對不一樣的屏幕進行必定程度的定製,但粒度較粗且功能有限,書寫起來也比較複雜。)前端
傳統方案帶來的弊端主要有:java
追求用戶體驗的道路沒有止境。爲了解決第一個問題,一些CDN廠商開始提供自適應的WebP圖片轉換技術,針對支持WebP的瀏覽器提供WebP格式圖片(例如花瓣網使用的又拍雲CDN)。web
而Service Worker做爲PWA核心技術,爲咱們帶來了一些新的思路。shell
Service Worker是一個註冊在指定源和路徑下的事件驅動Worker。它採用JavaScript控制關聯的頁面或者網站,攔截並修改訪問和資源請求,細粒度地緩存資源。你能夠徹底控制應用在特定情形(最多見的情形是網絡不可用)下的表現。 MDN
下圖是Service Worker的支持狀況。移動端方面Android的支持度較好,iOS Safari從11.3版本開始支持。整體支持度在84%(受地域和目標用戶羣體等方面的因素,通過統計,咱們產品的設備支持度爲63%)。
瀏覽器
Service Worker的具體使用方法這裏再也不贅述。接下來將重點介紹如何使用Service Worker對圖片請求作更細粒度的控制,達到漸進式優化的目標。緩存
WebP是一種新的圖像格式,它爲Web中的圖像提供更好的無損壓縮和有損壓縮。使用WebP能夠建立更小、更豐富的圖像。
WebP無損圖像的尺寸比PNG小26%,有損圖像比相同SSIM指數的JPEG圖像小25-34%。 Google
下圖是WebP的兼容性一覽。做爲Google提出的一項技術,WebP在Android端有很好的支持,但目前的兼容性還沒法在移動端網頁中普遍使用。默認狀況下,咱們仍然須要根據實際狀況選用恰當的圖片格式,在PNG/GIF/JPEG中進行取捨(例如,對於無透明度且色彩豐富的圖片,一般會選用壓縮比較高的JPEG格式)。服務器
而在使用Service Worker的網站中,因爲咱們能夠攔截和修改網絡請求,所以能夠針對支持WebP格式的瀏覽器,修改成請求對應的WebP圖片連接;而在不支持的瀏覽器中依然請求原連接。從而達到向後兼容式的優化。網絡
// sw.js self.addEventListener('fetch', (event) => { const req = event.request.clone() let newUrl = req.url if (!isImgRequest(newUrl)) { return } // 若是瀏覽器支持webp格式,則請求webp格式的圖片 const acceptHeader = req.headers.get('accept') const supportWebp = acceptHeader && acceptHeader.includes('webp') if (supportWebp) { newUrl = getWebpUrl(newUrl) } // 請求處理過的圖片連接 event.respondWith( fetch(newUrl, { mode: 'no-cors' }) ) }) function isImgRequest(url) { // 根據url判斷當前是不是須要優化的圖片請求 } function getWebpUrl(url) { // 根據圖片url獲取到對應的webp格式url(通常的圖片服務器都會提供獲取圖片格式轉換參數) }
對於使用rem方案來實現自適應佈局的網站來講,圖片的展現寬高也會根據實際狀況進行縮放。但因爲圖片一般採用固定的實際尺寸(例如使用2x圖),當圖片被縮放時,在小屏幕上會產生浪費,在大屏中效果卻被打了折扣。若是咱們可以根據不一樣機型,獲取匹配實際物理分辨率的圖片進行展現,則可針對每種設備最大程度的優化用戶體驗。
假設網站使用的圖片存儲服務提供了針對圖片的縮放接口(例如,圖片https://domain/key
能夠經過添加參數來將寬/高像素數減小到原來的0.6倍:https://domain/key/thumbnail/!60p
)。針對支持Service Worker的瀏覽器,就能夠根據設備的分辨率修改圖片請求的縮放參數,從而達到設備粒度的圖片尺寸定製。
這裏須要注意的是Service Worker做爲一種特殊的Worker不能直接操做DOM,全局做用域(經過self
關鍵字訪問)上也只有部分和window對象相同的屬性和方法。所以咱們須要經過Service Worker與網頁之間的通訊來獲取到當前設備的屏幕尺寸和DPI等信息。代碼示例以下:
// 網頁代碼 navigator.serviceWorker.controller.postMessage({ deviceWidth: window.screen.width * window.devicePixelRatio }) // sw.js const TRIPLE_PIXEL = 1242 // 3x橫向像素數 let deviceWidth = 0 self.addEventListener('fetch', (event) => { const req = event.request.clone() let newUrl = req.url if (!isImgRequest(newUrl)) { return } // 若是有屏幕分辨率信息,爲請求url添加縮放參數 if (deviceWidth) { const ratio = Math.round(deviceWidth / TRIPLE_PIXEL * 100) if (ratio > 0 && ratio < 100) { newUrl = getThumbnailUrl(newUrl, ratio) } } // 請求處理過的圖片連接 event.respondWith( fetch(newUrl, { mode: 'no-cors' }) ) }) self.addEventListener('message', ({ data }) => { deviceWidth = data.deviceWidth }) function getThumbnailUrl(url, ratio) { // 返回添加縮放參數的圖片url }
除了使用縮放參數,咱們也能夠分別導出1x/2x/3x圖(默認使用2x圖),根據DPI信息將圖片請求替換爲對應的<n>x圖便可。
Service Worker的功能如此強大,目前經常使用的情形已經有離線訪問、構建App Shell等。期待將來Service Worker以及其它PWA技術可以不斷帶給咱們新的思路。