PWA 時代的移動端圖片優化新思路

圖片一般是移動端網頁中最佔流量的資源。在不少類型的網站中(好比電商、社區),圖片佔據了網頁的大部分空間。優化圖片的質量和加載速度成爲提升用戶體驗的重要途徑。javascript

傳統方案的弊端

目前有一些被普遍應用的方案,例如選擇壓縮比更高的圖片格式、使用雪碧圖、HTTP二、CDN等。5年前的一條知乎回答前端開發中,對圖片的優化技巧有哪些? - 賀師俊的回答 - 知乎如今依然有效,這些方案在各大網站中仍然發揮着重要的做用。html

隨着移動端興起,各類移動設備帶來了豐富的機型尺寸和像素密度,逐漸暴露出以上方案共同的問題,即都是基於網站粒度的通用方案。咱們沒法保證在每種機型上都達到最優的效果,只能經過一些取捨來達到較均衡的方案,從而知足大部分用戶的體驗需求。(雖然經過Media Query等方式能夠對不一樣的屏幕進行必定程度的定製,但粒度較粗且功能有限,書寫起來也比較複雜。)前端

傳統方案帶來的弊端主要有:java

  • 爲了兼容舊版瀏覽器,使用新版瀏覽器的用戶沒法享受新技術帶來的更優體驗。(例如沒法使用具備更高壓縮比的WebP格式。)
  • 一般採用的2x圖方案對小屏幕尺寸是一種浪費,而在三倍屏上效果又不夠好。

追求用戶體驗的道路沒有止境。爲了解決第一個問題,一些CDN廠商開始提供自適應的WebP圖片轉換技術,針對支持WebP的瀏覽器提供WebP格式圖片(例如花瓣網使用的又拍雲CDN)。web

而Service Worker做爲PWA核心技術,爲咱們帶來了一些新的思路。shell

Service Worker

Service Worker是一個註冊在指定源和路徑下的事件驅動Worker。它採用JavaScript控制關聯的頁面或者網站,攔截並修改訪問和資源請求,細粒度地緩存資源。你能夠徹底控制應用在特定情形(最多見的情形是網絡不可用)下的表現。 MDN

下圖是Service Worker的支持狀況。移動端方面Android的支持度較好,iOS Safari從11.3版本開始支持。整體支持度在84%(受地域和目標用戶羣體等方面的因素,通過統計,咱們產品的設備支持度爲63%)。
sw支持狀況瀏覽器

Service Worker的具體使用方法這裏再也不贅述。接下來將重點介紹如何使用Service Worker對圖片請求作更細粒度的控制,達到漸進式優化的目標。緩存

使用WebP

WebP是一種新的圖像格式,它爲Web中的圖像提供更好的無損壓縮和有損壓縮。使用WebP能夠建立更小、更豐富的圖像。
WebP無損圖像的尺寸比PNG小26%,有損圖像比相同SSIM指數的JPEG圖像小25-34%。 Google

下圖是WebP的兼容性一覽。做爲Google提出的一項技術,WebP在Android端有很好的支持,但目前的兼容性還沒法在移動端網頁中普遍使用。默認狀況下,咱們仍然須要根據實際狀況選用恰當的圖片格式,在PNG/GIF/JPEG中進行取捨(例如,對於無透明度且色彩豐富的圖片,一般會選用壓縮比較高的JPEG格式)。服務器

webp兼容性

而在使用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(通常的圖片服務器都會提供獲取圖片格式轉換參數)
}

有關DPI自適應的思考

對於使用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技術可以不斷帶給咱們新的思路。

相關文章
相關標籤/搜索