在任何地方都能快速載入的網站開發作業可能很棘手。裝置功能琳瑯滿目,連線網路的品質也參差不齊,這讓開發人員覺得難以克服。雖然我們可以利用瀏覽器功能提升載入效能,但如何得知使用者裝置的功能,或網路連線品質?解決方法是用戶端提示!
用戶端提示是一組選擇加入的 HTTP 要求標頭,可讓我們深入瞭解使用者裝置和連線網路的這些層面。透過輕觸這項伺服器端資訊,我們可以根據裝置和/或網路狀況,變更內容的傳送方式。協助我們打造更具包容性的使用者體驗。
一切都與內容交涉有關
用戶端提示是另一種內容交涉方法,也就是根據瀏覽器要求標頭變更內容回應。
內容交涉的一個例子是涉及
Accept
要求標頭。這項標頭說明瀏覽器可理解的內容類型,伺服器可使用這些類型協商回應。如果是圖片要求,Chrome 的 Accept 標頭內容如下:
Accept: image/webp,image/apng,image/*,*/*;q=0.8
雖然所有瀏覽器都支援 JPEG、PNG 和 GIF 等圖片格式,但 Accept 在這個案例中表示,瀏覽器也支援 WebP 和 APNG。我們可運用這項資訊,為每個瀏覽器協商最佳圖片類型:
<?php
// Check Accept for an "image/webp" substring.
$webp = stristr($_SERVER["HTTP_ACCEPT"], "image/webp") !== false ? true : false;
// Set the image URL based on the browser's WebP support status.
$imageFile = $webp ? "whats-up.webp" : "whats-up.jpg";
?>
<img src="<?php echo($imageFile); ?>" alt="I'm an image!">
與 Accept 類似,用戶端提示也是協商內容的另一種方式,但會考量裝置功能和網路狀況。有了用戶端提示,我們就能根據個別使用者的體驗做出伺服器端效能決策,例如決定是否要向網路狀況不佳的使用者提供非重要資源。本指南將說明所有可用的提示,以及如何運用這些提示,讓內容傳送方式更貼近使用者需求。
啟用
與 Accept 標頭不同,用戶端提示不會自動顯示 (Save-Data 除外,我們稍後會討論)。為盡量減少要求標頭,您必須在使用者要求資源時傳送 Accept-CH 標頭,選擇要接收的用戶端提示:
Accept-CH: Viewport-Width, Downlink
Accept-CH 的值是以半形逗號分隔的清單,列出網站將用於判斷後續資源要求結果的提示。當用戶端讀取這個標頭時,會收到「這個網站需要 Viewport-Width 和 Downlink 用戶端提示」的訊息。請不必擔心具體提示本身。我們稍後會說明這些內容。
您可以使用任何後端語言設定這些選擇加入標頭。舉例來說,可以使用 PHP 的 header 函式。您甚至可以在 <meta> 代碼上使用http-equiv 屬性設定這些選擇加入標頭:
<meta http-equiv="Accept-CH" content="Viewport-Width, Downlink" />
所有用戶端提示!
用戶端提示描述的內容有兩種:使用者使用的裝置,以及他們用來存取您網站的網路。我們來簡單介紹所有可用的提示。
裝置提示
部分用戶端提示會描述使用者裝置的特徵,通常是螢幕特徵。其中有些屬性可協助您為特定使用者的螢幕選擇最佳媒體資源,但並非所有屬性都以媒體為中心。
在開始介紹這份清單之前,建議先瞭解幾個用來描述螢幕和媒體解析度的重要術語:
內建大小:媒體資源的實際尺寸。舉例來說,如果您在 Photoshop 中開啟圖片,圖片大小對話方塊中顯示的尺寸就是內建大小。
密度校正後的固有尺寸:媒體資源經過像素密度校正後的尺寸。這是圖片的內建大小除以裝置像素比例。 舉例來說,假設有以下標記:
<img
src="whats-up-1x.png"
srcset="whats-up-2x.png 2x, whats-up-1x.png 1x"
alt="I'm that image you wanted."
/>
假設本例中 1x 圖片的內建大小為 320x240,而 2x 圖片的內建大小為 640x480。如果用戶端剖析了這項標記,且用戶端安裝在螢幕裝置像素比例為 2 的裝置上 (例如 Retina 螢幕),系統就會要求提供 2x 圖片。由於 640x480 除以 2 是 320x240,因此2x圖片的密度校正內建大小為 320x240。
外部大小:CSS 和其他版面配置因素 (例如 width 和 height 屬性) 套用至媒體資源後的大小。假設您有一個 <img> 元素,會載入密度校正後的內建大小為 320x240 的圖片,但該元素也分別套用了值為 256px 和 192px 的 CSS width 和 height 屬性。在本例中,該 <img> 元素的外部大小會變成 256x192。
width: 256px; 和 height: 192px; 的 CSS 規則,可將 320x240 的內在大小圖片轉換為 256x192 的外在大小圖片。瞭解一些術語後,我們來看看可用的裝置專屬用戶端提示清單。
可視區域寬度
Viewport-Width 是使用者可視區域的寬度 (以 CSS 像素為單位):
Viewport-Width: 320
這項提示可與其他螢幕專屬提示搭配使用,針對特定螢幕大小提供最佳的圖片處理方式 (即裁剪) (即藝術指導),或省略目前螢幕寬度不需要的資源。
DPR
DPR 是裝置像素比例的縮寫,會回報使用者螢幕的實體像素與 CSS 像素比例:
DPR: 2
選取與螢幕像素密度相符的圖片來源時,這項提示非常實用 (例如 srcset 屬性中的 x 描述元)。
寬度
Width 提示會顯示在 <img> 或 <source> 標記使用 sizes 屬性觸發的圖片資源要求中。sizes 會告知瀏覽器資源的外部大小;Width 會使用該外部大小要求圖片,圖片的內部大小最適合目前的版面配置。
舉例來說,假設使用者要求寬度為 320 CSS 像素的網頁,DPR 為 2。裝置會載入含有 <img> 元素的文件,該元素包含 sizes 屬性值 85vw (即 所有螢幕大小的可視區域寬度皆為 85%。如果已選擇 Width 提示,用戶端會將此 Width 提示連同 <img> 的 src 要求傳送至伺服器:
Width: 544
在本例中,用戶端會向伺服器暗示,所要求圖片的最佳內建寬度為視埠寬度的 85% (272 像素) 乘以螢幕的 DPR (2),等於 544 像素。
這項提示特別強大,因為它不僅會考量經過密度校正的螢幕寬度,還會將這項重要資訊與版面配置中的圖片外部大小進行比對。伺服器可藉此機會,針對螢幕和版面配置協商最佳圖片回應。
Content-DPR
您已經知道螢幕有裝置像素比例,但資源也有自己的像素比例。在最簡單的資源選取應用情境中,裝置和資源之間的像素比例可以相同。但!如果同時使用 DPR 和 Width 標頭,資源的外部大小可能會導致兩者不同。這時,Content-DPR 提示就派上用場了。
與其他用戶端提示不同,Content-DPR 不是伺服器使用的要求標頭,而是伺服器必須在選取資源時使用 DPR 和 Width 提示傳送的回應標頭。Content-DPR 的值應為下列方程式的結果:
Content-DPR = [所選圖片資源大小] / ([Width] / [DPR])
傳送 Content-DPR 要求標頭時,瀏覽器會知道如何根據螢幕的裝置像素比例和版面配置,縮放指定圖片。如果沒有這項資訊,圖片可能無法正確縮放。
Device-Memory
從技術上來說,Device-Memory 是裝置記憶體 API 的一部分,可顯示目前裝置的大約記憶體容量 (以 GiB 為單位):
Device-Memory: 2
這個提示的可能用途是減少傳送至記憶體有限裝置上瀏覽器的 JavaScript 數量,因為 JavaScript 是瀏覽器通常載入的資源密集型內容類型。或者,您也可以傳送較低的 DPR 圖片,因為解碼時使用的記憶體較少。
網路提示
網路資訊 API 提供另一類用戶端提示,說明使用者網路連線的效能。我認為這些提示最實用。我們可以根據連線速度,調整向用戶端傳送資源的方式,為連線速度緩慢的使用者提供量身打造的體驗。
即時文字訊息
RTT 提示會提供應用程式層的約略封包往返時間 (以毫秒為單位)。與傳輸層 RTT 不同,RTT 提示包含伺服器處理時間。
RTT: 125
由於延遲在載入效能中扮演的角色,這項提示非常實用。使用 RTT 提示,我們可以根據網路回應速度做出決策,這有助於加快整體體驗的傳送速度 (例如省略部分要求)。
下行
延遲時間對載入效能很重要,但頻寬也有影響。Downlink 提示以每秒百萬位元 (Mbps) 為單位,顯示使用者連線的約略下游速度:
Downlink: 2.5
搭配 RTT 使用 Downlink,有助於根據網路連線品質,變更向使用者傳送內容的方式。
ECT
ECT 提示代表有效連線類型。這個值是列舉的連線類型清單之一,每個類型都說明指定範圍內的 RTT 和 Downlink 值之間的連線。
這個標頭不會說明實際連線類型,例如不會回報閘道是行動通信基地台還是 Wi-Fi 存取點。而是分析目前連線的延遲和頻寬,判斷最符合哪個網路設定檔。舉例來說,如果您透過 Wi-Fi 連線到速度較慢的網路,ECT 可能會填入 2g 值,這是有效連線最接近的近似值:
ECT: 2g
ECT 的有效值為 4g、3g、2g 和 slow-2g。這個提示可做為評估連線品質的起點,隨後可使用 RTT 和 Downlink 提示進行調整。
Save-Data
Save-Data 並非描述網路狀況的提示,而是使用者偏好設定,指出網頁應傳送較少資料。
我偏好將 Save-Data 分類為網路提示,因為您會對其執行的許多操作,都與其他網路提示類似。使用者也可能在高延遲/低頻寬環境中啟用這項功能。如果存在,這個提示一律如下所示:
Save-Data: on
在 Google,我們已討論過如何使用 Save-Data。這類節目可能會對觀眾造成強烈衝擊。這項信號表示使用者要求您減少傳送內容。如果您能傾聽並根據這項信號採取行動,使用者會非常感謝。
融會貫通並靈活運用
如何運用用戶端提示取決於您。由於這些報表提供大量資訊,因此您有許多選擇。為了激發一些想法,我們來看看用戶端提示可以為虛構的木材公司「Sconnie Timber」做些什麼。這家公司位於中西部上游的農村地區。偏遠地區的網路連線通常不穩定,這時,用戶端提示等技術就能真正為使用者帶來不同體驗。
回應式圖片
除了最簡單的回應式圖片用途外,其他用途都可能變得複雜。如果針對不同螢幕大小和不同格式,您有同一張圖片的多種處理方式和變化版本,該怎麼辦?該標記很快就會變得非常複雜非常。
很容易出錯,也容易忘記或誤解重要概念 (例如 sizes)。
雖然 <picture> 和 srcset 無疑是絕佳工具,但針對複雜用途開發及維護這些工具可能相當耗時。我們可以自動生成標記,但這項作業也很困難,因為 <picture> 和 srcset 提供的功能相當複雜,因此自動化作業必須以維持彈性的方式進行。
用戶端提示可簡化這項程序。與用戶端提示協商圖片回應可能如下所示:
- 如果工作流程適用,請先勾選
Viewport-Width提示,選取圖片處理方式 (即經過藝術指導的圖像)。 - 查看
Width提示和DPR提示,然後選擇符合圖片版面配置大小和螢幕密度的來源 (類似於srcset中的x和w描述元),選取圖片解析度。 - 選取瀏覽器支援的最佳檔案格式 (大部分瀏覽器會協助我們完成這項操作)。
Accept
就我虛構的木材公司客戶而言,我使用用戶端提示,在 PHP 中開發了簡單的自適應圖片選取常式。這表示系統不會將這個標記傳送給所有使用者,而是:
<picture>
<source
srcset="
company-photo-256w.webp 256w,
company-photo-512w.webp 512w,
company-photo-768w.webp 768w,
company-photo-1024w.webp 1024w,
company-photo-1280w.webp 1280w
"
type="image/webp"
/>
<img
srcset="
company-photo-256w.jpg 256w,
company-photo-512w.jpg 512w,
company-photo-768w.jpg 768w,
company-photo-1024w.jpg 1024w,
company-photo-1280w.jpg 1280w
"
src="company-photo-256w.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="The Sconnie Timber Staff!"
/>
</picture>
根據個別瀏覽器支援情況,我將其縮減為以下內容:
<img
src="/image/sizes:true/company-photo.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="SAY CHEESY PICKLES."
/>
在本範例中,/image 網址是 PHP 指令碼,後面接著由 mod_rewrite 重新編寫的參數。這項函式會採用圖片檔案名稱和其他參數,協助後端指令碼在特定條件下選擇最佳圖片。
我猜你第一個問題是「這不只是在後端重新實作 <picture> 和 srcset 嗎?」
從某種意義上來說,是的,但有一個重要區別:應用程式使用用戶端提示製作媒體回應時,大部分 (如果不是全部) 的工作都更容易自動化,包括可代表您執行這項作業的服務 (例如 CDN)。但使用 HTML 解決方案時,必須為每個用途編寫新的標記。當然可以,你可以自動產生標記。不過,如果設計或需求有變,您可能需要在日後重新檢視自動化策略。
用戶端提示可讓您從無損的高解析度圖片開始,然後動態調整大小,以配合任何螢幕和版面配置組合。與 srcset 不同,這個方法可讓您列舉瀏覽器可選擇的可能圖片候選項目固定清單,因此更具彈性。srcset 會強制您為瀏覽器提供一組粗略的變體 (例如 256w、512w、768w 和 1024w),但以用戶端提示為基礎的解決方案可提供所有寬度,無須大量標記。
當然,您不必自行撰寫圖片選取邏輯。使用 w_auto 參數時,Cloudinary 會使用用戶端提示製作圖片回應,並發現使用支援用戶端提示的瀏覽器時,中位數使用者下載的位元組減少了 42%。
但請注意,Chrome 67 電腦版的變更已移除對跨來源用戶端提示的支援。 幸好,這些限制不會影響 Chrome 的行動版,而且功能政策完成後,所有平台都會解除限制。
協助使用慢速網路的使用者
適應性效能的概念是,我們可以根據用戶端提示提供的資訊調整資源的傳送方式,特別是使用者網路連線目前狀態的相關資訊。
就 Sconnie Timber 的網站而言,我們會在網路速度緩慢時採取步驟,減輕負載,並在後端程式碼中檢查 Save-Data、ECT、RTT 和 Downlink 標頭。完成後,我們會產生網路品質分數,用來判斷是否應介入,以提供更優質的使用者體驗。這個網路分數介於 0 和 1 之間,0 代表網路品質最差,1 則代表網路品質最佳。
一開始,我們會檢查 Save-Data 是否存在。如果是,分數會設為 0,因為我們假設使用者希望我們盡一切努力,讓體驗更輕巧快速。
不過,如果沒有 Save-Data,我們會繼續進行,並根據 ECT、RTT 和 Downlink 提示的值計算分數,說明網路連線品質。網路分數生成原始碼位於 Github。總而言之,如果我們以某種方式使用網路相關提示,就能為網路速度緩慢的使用者提供更優質的體驗。
如果網站能根據用戶端提示提供的資訊進行調整,我們就不必採取「全有或全無」的做法。我們可以智慧地決定要傳送哪些資源。我們可以修改回應式圖片選取邏輯,在網路品質不佳時,針對特定螢幕傳送畫質較低的圖片,以加快載入速度。
在這個範例中,我們可以瞭解用戶端提示在提升較慢網路上的網站效能時,所帶來的影響。以下是網站的 WebPagetest 瀑布圖,該網站位於慢速網路上,且不會根據用戶端提示調整:
現在,同一網站在同一個緩慢連線上的瀑布圖,但這次網站使用用戶端提示來排除非重要網頁資源:
用戶端提示將網頁載入時間從 45 秒以上縮短至十分之一以下。在這種情況下,用戶端提示的優點再怎麼強調都不為過,對於透過緩慢網路尋找重要資訊的使用者來說,這項功能可說是天賜良機。
此外,您可以使用用戶端提示,不會影響不支援提示的瀏覽器體驗。舉例來說,如果我們想使用 ECT 提示的值調整資源傳送,同時仍為不支援的瀏覽器提供完整體驗,可以將預設值設為類似下列的值:
// Set the ECT value to "4g" by default.
$ect = isset($_SERVER["HTTP_ECT"]) ? $_SERVER["HTTP_ECT"] : "4g";
這裡的 "4g" 代表 ECT 標頭所描述的最高品質網路連線。如果我們將 $ect 初始化為 "4g",不支援用戶端提示的瀏覽器就不會受到影響。選擇採用 FTW!
請留意快取!
根據 HTTP 標頭變更回應時,請務必瞭解快取會如何處理該資源的後續擷取作業。Vary 標頭在此不可或缺,因為它會將快取項目鍵入提供給它的要求標頭值。簡單來說,如果您根據指定的 HTTP 要求標頭修改任何回應,幾乎都應該在 Vary 中加入該要求標頭,如下所示:
Vary: DPR, Width
不過,這項做法有重大限制:您絕不應在經常變更的標頭 (例如 Cookie) 上 Vary 可快取的回應,因為這些資源實際上會變成無法快取。瞭解這點後,您可能會想避免Vary依附於 RTT 或 Downlink 等用戶端提示標頭,因為這些是可能經常變更的連線因素。如要修改這些標頭的回應,請考慮只鍵入 ECT 標頭,這樣可減少快取遺漏。
當然,前提是您一開始就要快取回應。
舉例來說,如果 HTML 資產的內容是動態的,您就不會快取這些資產,因為這可能會導致使用者重複造訪時體驗中斷。在這種情況下,請隨意修改這類回覆,不必擔心 Vary。
服務工作人員中的用戶端提示
內容協商不再只是伺服器的專利!由於服務工作人員會充當用戶端與伺服器之間的 Proxy,因此您可以透過 JavaScript 控制資源的傳送方式。包括用戶端提示。在服務工作人員的 fetch 事件中,您可以使用 event 物件的 request.headers.get 方法讀取資源的要求標頭,如下所示:
self.addEventListener('fetch', (event) => {
let dpr = event.request.headers.get('DPR');
let viewportWidth = event.request.headers.get('Viewport-Width');
let width = event.request.headers.get('Width');
event.respondWith(
(async function () {
// Do what you will with these hints!
})(),
);
});
您選擇加入的任何用戶端提示標頭,都可以透過這種方式讀取。不過,您也可以透過其他方式取得部分資訊。您可以在 navigator 物件中,透過下列對應的 JavaScript 屬性讀取網路專屬提示:
| 用戶端提示 | 對應的 JS 程式碼 |
|---|---|
| `ECT` | `navigator.connection.effectiveType` |
| `RTT` | `navigator.connection.rtt` |
| `Save-Data` | `navigator.connection.saveData` |
| `Downlink` | `navigator.connection.downlink` |
| `Device-Memory` | `navigator.deviceMemory` |
由於這些 API 並非在所有地方都適用,因此您需要使用 in
運算子進行功能檢查:
if ('connection' in navigator) {
// Work with netinfo API properties in JavaScript!
}
從這裡開始,您可以使用與伺服器上類似的邏輯,但不需要伺服器來與用戶端提示協商內容。單靠 Service Worker 就能加快速度並提升體驗的穩定性,因為 Service Worker 能夠在使用者離線時提供內容。
總結
有了用戶端提示,我們就能以完全漸進的方式,為使用者提供更快速的體驗。我們可以根據使用者的裝置功能放送媒體,讓放送回應式圖片比依賴 <picture> 和 srcset 更容易,尤其是在複雜的應用實例中。這不僅能減少開發時間和精力,還能以比
更重要的是,我們可以偵測網路連線品質不佳的情況,並修改傳送內容和方式,為使用者彌補連線落差。這有助於讓網路不穩定的使用者輕鬆存取網站。搭配服務工作人員,我們可以建立速度極快的網站,甚至在離線時也能使用。
雖然用戶端提示僅適用於 Chrome 和 Chromium 架構的瀏覽器,但您仍可使用這些提示,不會對不支援提示的瀏覽器造成負擔。建議使用用戶端提示,打造真正包容且適應性強的體驗,瞭解每位使用者的裝置功能和連線網路。希望其他瀏覽器供應商能瞭解這些功能的價值,並有意願實作。
資源
- 使用用戶端提示自動產生回應式圖片
- 用戶端提示和回應式圖片:Chrome 67 的變更
- 取得 (用戶端) 提示! (簡報)
- 使用
Save-Data提供快速輕巧的應用程式
感謝 Ilya Grigorik、Eric Portis、Jeff Posnick、Yoav Weiss 和 Estelle Weyl 對本文提供寶貴意見並進行編輯。