使用用戶端提示自動選取資源

Ilya Grigorik

打造網路環境可帶來無與倫比的觸及率。無論品牌或平台為何,您只要按一下即可使用網頁應用程式,幾乎所有連線的裝置智慧型手機、平板電腦、筆電、桌上型電腦、電視等都可以使用。為了提供最佳體驗,您建構的回應式網站可以調整每種板型規格的呈現方式和功能,而現在正執行效能檢查清單,以確保應用程式盡快載入:您已最佳化關鍵轉譯路徑,您已壓縮及快取大部分的文字資源,而現在您經常查看圖片資源的位元組。發生問題,圖片最佳化並不容易

  • 決定適當的格式 (向量與光柵)
  • 判斷最佳的編碼格式 (jpeg、webp 等)
  • 決定正確的壓縮設定 (有損與無失真)
  • 決定應保留或移除哪些中繼資料
  • 為每個螢幕 + DPR 解析度建立多個變化版本
  • ...
  • 考量使用者的網路類型、速度和偏好

這些都是熟知問題,這些群組共同建立了一個大型最佳化空間,我們 (開發人員) 常會加以忽略或忽略。人類會反覆探索相同的搜尋空間,不良行為在牽涉許多步驟時更是如此。另一方面,電腦則無法妥善處理這類工作。

對於映像檔及其他具有類似屬性的資源,要找出理想且永續的最佳化策略,答案很簡單:自動化。如果您打算調整資源,就可能發生錯了,畢竟您難免會出錯,或是其他人會這樣犯下這些錯誤 - 保證。

注重效能的開發人員

透過圖片最佳化空間進行搜尋時,系統會分為兩個不同的階段:建構時間和執行階段。

  • 某些最佳化是資源本身的內建函式,例如選取適當的格式和編碼類型、調整每個編碼器的壓縮設定、移除不必要的中繼資料等等。您可以在「建構時間」執行這些步驟。
  • 其他最佳化項目取決於要求要求的用戶端類型和屬性,且必須在「執行階段」執行:為用戶端的 DPR 和預期顯示寬度選取適當資源,考量用戶端的網路速度、使用者和應用程式偏好設定等。

建構時間工具已存在,但情況尚有改善。舉例來說,雖然動態調整每張圖片和每張圖片格式的「品質」設定可以節省大量成本,但我還沒有發現在研究以外用到的人。這是推動創新的領域,但為了教學目的,我會先留下來。我們先來看看故事的執行時間部分。

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

應用程式意圖非常簡單:擷取並顯示圖片 (佔使用者可視區域的 50%)。大多數設計師都會對酒吧擦洗雙手,同時,團隊中註重效能的開發人員度過了很長的夜晚:

  1. 為了取得最佳壓縮效果,她想要針對每個用戶端使用最佳圖片格式:WebP (適用於 Chrome)、JPEG XR (適用於 Edge),以及 JPEG (其他) 格式。
  2. 為了提供最佳視覺品質,她需要為每個解析度各產生多個變化版本:1 倍、1.5 倍、2 倍、2.5 倍、3 倍,甚至是兩者之間。
  3. 為避免提供不必要的像素,她必須瞭解「使用者可視區域的 50%」實際上代表的意義,而且這邊有很大的可視區域寬度。
  4. 在理想情況下,她還想提供彈性,讓連線速度較慢的使用者會自動擷取較低的解析度。畢竟,現在該是時候喝杯了。
  5. 應用程式也會公開一些使用者控制項,從而影響應擷取的圖片資源,因此也要考量這一點。

另外,設計人員也發現,如果可視區域尺寸較小,為了達到最佳易讀性,她需要以 100% 的寬度顯示其他圖片。也就是說,我們現在必須針對另一個資產重複相同的程序,然後再針對可視區域大小進行擷取。我有說過這東西很難嗎?好,讓我們實現吧picture 元素將獲得許多實用成果

<picture>
    <!-- serve WebP to Chrome and Opera -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
    <!-- serve JPEGXR to Edge -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
        /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
        /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
        /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
        /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <!-- serve JPEG to others -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
    <!-- fallback for browsers that don't support picture -->
    <img src="/image/thing.jpg" width="50%">
</picture>

我們已處理圖片方向、格式選擇,以及每個圖片的六個變化版本,因應用戶端裝置 DPR 和可視區域寬度的變化。引人矚目!

遺憾的是,picture 元素不允許根據用戶端的連線類型或速度定義任何規則行為規則。也就是說,在某些情況下,其處理演算法確實允許使用者代理程式調整其擷取的資源,請參閱步驟 5。我們再也希望使用者代理程式更聰明(注意:目前未採用任何實作方式)。同樣地,picture 元素中沒有掛鉤,可供根據應用程式或使用者偏好設定區分的應用程式專屬邏輯。如要取得最後兩個位元,必須將上述所有邏輯移至 JavaScript,但這樣會失去 picture 提供的預先載入掃描器最佳化項目。嗯。

除了這些限制之外,這種做法可以正常運作。至少對這個特定資產而言真正的長期挑戰在於,我們無法預期設計人員或開發人員為每個資產手動編寫類似程式碼。第一次嘗試是很有趣的大腦謎題,但之後就會立即失去吸引力。我們需要自動化功能。也許 IDE 或其他內容轉換工具可以節省我們並自動產生上方的樣板。

透過用戶端提示自動選取資源

深呼吸,暫停您的信念,現在請思考以下範例:

<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
    <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
    <img sizes="100vw" src="/image/thing-crop">
</picture>

相信,上述範例能夠提供與上述內容較長的圖片標記相同的所有功能,如圖所示,這種做法能讓開發人員完全掌控圖片資源的擷取方式、方式和擷取時機。「魔術」位於啟用用戶端提示報表的第一行,它會指示瀏覽器宣傳裝置像素比例 (DPR)、版面配置可視區域寬度 (Viewport-Width),以及資源的預期顯示寬度 (Width)。

啟用用戶端提示後,產生的用戶端標記只會保留呈現方式需求。設計人員無需擔心圖片類型、用戶端解析度、減少傳送位元組數的最佳中斷點或其他資源選取條件。以為看他們從未做過 他們不應該是如此更棒的是,開發人員也不需要重新編寫及擴充上述標記,因為實際資源選擇是由用戶端和伺服器協商。

Chrome 46 提供對 DPRWidthViewport-Width 提示的原生支援。提示預設為停用,上方的 <meta http-equiv="Accept-CH" content="..."> 則是選擇加入信號,用來指示 Chrome 將指定標頭附加至傳出要求。完成上述步驟後,讓我們檢查範例圖片要求的要求和回應標頭:

用戶端提示協商流程圖

Chrome 會透過「Accept 要求」標頭來宣傳 WebP 格式支援;新的 Edge 瀏覽器同樣會透過「接受」標頭,支援 JPEG XR。

接下來的三個要求標頭是用戶端提示標頭,用來宣傳用戶端裝置的裝置像素比例 (3x)、版面配置可視區域寬度 (460px),以及預定資源的顯示寬度 (230 像素)。這會向伺服器提供所有必要資訊,方便伺服器根據本身的政策組合選取最佳映像檔變化版本,像是預先產生的資源可用性、重新編碼或調整資源大小、資源熱門程度、目前伺服器負載等。在這個特定情況下,伺服器會使用 DPRWidth 提示,並傳回 WebP 資源,如 Content-TypeContent-DPRVary 標頭所示。

這裡還沒有什麼魔法。我們已將資源選取功能從 HTML 標記移至用戶端與伺服器之間的要求/回應交涉。因此,HTML 只在考量呈現要求時需要的,也就是我們可信任任何設計人員和開發人員撰寫的事項,而透過圖片最佳化空間進行搜尋時,則需等到進行圖片最佳化作業後,才能輕鬆大規模進行自動化作業。還記得我們注重成效的開發人員嗎?她的工作是編寫一個能利用所提供的提示,並傳回適當的回應的影像服務:她可以使用她喜歡的任何語言或伺服器,或讓第三方服務或 CDN 代表她執行此操作。

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

還記得上面的這傢伙嗎?由用戶端提示,微風圖片代碼現已改採 DPR、可視區域和寬度感知,不需要任何額外標記。如果需要新增藝術方向,您可以使用 picture 標記 (如上文所示),否則現有的所有圖片標記變得更聰明瞭。用戶端提示可以強化現有的 imgpicture 元素。

透過 Service 工作站控制資源選擇

ServiceWorker 實際上是在瀏覽器中執行的用戶端 Proxy。攔截所有傳出要求,並可讓您檢查、重新寫入、快取,甚至是合成回應。圖片是一樣的,在啟用用戶端提示後,有效的 ServiceWorker 即可識別圖片要求、檢查提供的用戶端提示,以及定義自己的處理邏輯。

self.onfetch = function(event) {
    var req = event.request.clone();
    console.log("SW received request for: " + req.url)
    for (var entry of req.headers.entries()) {
    console.log("\t" + entry[0] +": " + entry[1])
    }
    ...
}
用戶端提示:serviceWorker。

ServiceWorker 可讓您完全在用戶端控管資源選擇。這非常重要。允許接收器進入,因為可能存在無限可能:

  • 您可以重新編寫使用者代理程式設定的用戶端提示標頭值。
  • 您可以在要求中附加新的用戶端提示標頭值。
  • 您可以重寫網址,並將圖片要求指向替代伺服器 (例如 CDN)。
    • 如果這樣更容易在基礎架構中部署,您甚至可以將提示值從標頭和網址移至本身。
  • 您可以快取回應並定義提供資源的邏輯。
  • 您可以根據使用者的連線調整回應內容。
  • 您可以考量應用程式和使用者偏好設定覆寫情況。
  • 你可以做自己喜歡的任何事情,

picture 元素在 HTML 標記中提供必要的藝術方向控制項。用戶端提示會針對產生的圖片要求提供註解,以便啟用資源選取自動化功能。ServiceWorker 可針對用戶端提供要求和回應管理功能。這部擴充式網頁實際運作,

客戶提示常見問題

  1. 有哪些用戶端提示可用? 已於 Chrome 46 出貨。瞭解 FirefoxEdge 的注意事項。

  2. 為什麼客戶提示要選擇啟用? 我們可以盡量減少不使用用戶端提示的網站負擔,如要啟用用戶端提示,網站必須在網頁標記中提供 Accept-CH 標頭或對等的 <meta http-equiv> 指令。不論是這兩個方法,使用者代理程式都會將適當的提示附加到所有子資源要求中。日後,我們可能會提供其他機制,針對特定來源保留這項偏好設定,這樣就能透過導覽要求傳送相同的提示。

  3. 如果我們採用 ServiceWorker,為什麼需要用戶端提示?ServiceWorker 無法存取版面配置、資源和可視區域寬度資訊。至少,假如沒有導入昂貴的往返次數和大幅延遲圖片要求 (例如由預先載入剖析器啟動圖片要求) 時,就沒有例外。用戶端提示已與瀏覽器整合,讓這項資料成為要求的一部分。

  4. 用戶端提示是否僅適用於圖片資源?DPR、可視區域寬度和寬度提示的核心用途,是為圖片素材資源啟用資源選取功能。然而,無論類型為何 (例如 CSS 和 JavaScript 要求),系統會針對所有子資源提供相同的提示,且這類提示也可用來最佳化這些資源。

  5. 為什麼部分圖片要求不會回報寬度? 網站取決於圖片內建函式的尺寸,因此瀏覽器可能無法知道預期的顯示寬度。因此,如果是這類要求,以及沒有「顯示寬度」(例如 JavaScript 資源) 的要求,都會省略寬度提示。如要接收寬度提示,請務必在圖片上指定大小值

  6. <插入我最愛的提示> ServiceWorker 可讓開發人員攔截及修改 (例如新增標頭) 所有傳出要求。舉例來說,您可以輕鬆新增以 NetInfo 為基礎的資訊以表示目前的連線類型,詳情請參閱「透過 ServiceWorker 功能回報的功能」。Chrome 內建的「原生」提示 (DPR、Width、Resource-Width) 會延遲所有圖片要求,因此瀏覽器會導入這類提示。

  7. 哪裡可以瞭解詳情、查看更多示範?該怎麼辦? 請參閱說明文件。如有任何意見回饋或其他疑問,歡迎在 GitHub 上提出問題