HTTP網(wǎng)絡(luò)協(xié)議中的HTTP Client Hints 技術(shù)
最近幾年各種 Web 技術(shù)一直在爆炸式發(fā)展,每天都有大量新東西涌現(xiàn)出來(lái)。針對(duì)這個(gè)現(xiàn)象,業(yè)內(nèi)兩位大佬最近先后發(fā)文表達(dá)了自己的觀點(diǎn):Stop pushing the web forward、Is the web platform getting too big?。其實(shí)很早之前我就意識(shí)到以我目前的精力,吃透所有 Web 新技術(shù)幾乎是不可能完成的任務(wù),我關(guān)注新技術(shù)的側(cè)重點(diǎn)放在了性能優(yōu)化上。
今天我要向大家介紹的技術(shù)是:HTTP Client Hints,也與性能優(yōu)化有關(guān)。利用這項(xiàng)技術(shù),HTTP 客戶端(通常可以認(rèn)為是瀏覽器)能夠主動(dòng)將一些特性告訴服務(wù)端,以便服務(wù)端更有針對(duì)性地輸出內(nèi)容。這項(xiàng)技術(shù)由我們熟知的 Ilya Grigorik 提出,目前還處在較為早期的階段,較為正式的描述文檔可以在這里找到。目前 Chrome 46 (beta) 已支持它,IE 和 Firefox 則還在考慮中。
其實(shí)之前瀏覽器已經(jīng)將很多自身特性放在 HTTP 請(qǐng)求中,例如下面這些頭部字段:
User-Agent:提供瀏覽器類(lèi)型及版本、操作系統(tǒng)及版本、瀏覽器內(nèi)核等信息;
Accept:表明瀏覽器支持哪些 MIME type(例如 Chrome 通過(guò) Accept 表明自己支持 image/webp 圖片格式);
Accept-Encoding:表明本瀏覽器支持哪些內(nèi)容編碼方式(例如:gzip、deflate、sdch);
Accept-Language:表明本瀏覽器支持那些語(yǔ)言;
通過(guò)以上這些頭部字段,我們已經(jīng)可以針對(duì)不同客戶端輸出不同內(nèi)容。例如本博客對(duì)支持 Webp 格式的瀏覽器會(huì)使用 Webp 來(lái)減少圖片大小;本博客還會(huì)通過(guò) User-Agent 針對(duì) IE 老版本禁用 localStorage 緩存策略。
但是有一些瀏覽器特性,我們無(wú)法直接獲取,如屏幕分辨率、設(shè)備像素比(devicePixelRatio)、用戶帶寬等。而在移動(dòng) Web 中,為了盡可能節(jié)省用戶流量,需要輸出尺寸最合適的圖片資源。為了解決這個(gè)問(wèn)題,常見(jiàn)的方案有:1)使用 JS 獲取這些特性,動(dòng)態(tài)拼接圖片 URL;2)使用 HTML 中的 sizes 和 srcset 屬性、picture 標(biāo)簽或 CSS 中的 image-set 屬性來(lái)實(shí)現(xiàn)響應(yīng)式圖片。方案 1 很簡(jiǎn)單,這里略過(guò);方案 2 網(wǎng)上有很多相關(guān)文章,不熟悉的同學(xué)可以自行搜索「響應(yīng)式圖片」了解下。
這里看一個(gè)使用方案 2 中提到的 picture、sizes 和 srcset 實(shí)現(xiàn)的響應(yīng)式圖片代碼(via):
HTMLmedia="(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"> 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"> 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"> 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"> 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"> 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">
這段冗長(zhǎng)的代碼只是為了實(shí)現(xiàn)一張響應(yīng)式圖片,盡管有一些夸張,實(shí)際使用時(shí)一般不會(huì)寫(xiě)這么全,但從中可以得到一個(gè)結(jié)論:在客戶端實(shí)現(xiàn)的策略越多,HTML 體積就越大越冗余,可維護(hù)性和可讀性就越差。
而使用了 HTTP Client Hints 之后,瀏覽器在頁(yè)面發(fā)起子資源請(qǐng)求時(shí),會(huì)通過(guò)新增的一系列頭部字段帶上分辨率、設(shè)備像素比、圖片寬度等信息,使得各種復(fù)雜的策略可以挪到服務(wù)端去實(shí)現(xiàn)了。下面來(lái)看一看具體細(xì)節(jié):
首先,有了支持 HTTP Client Hints 的瀏覽器之后,頁(yè)面上還需要顯式啟用它。這是因?yàn)椴皇撬蟹?wù)端都實(shí)現(xiàn)了響應(yīng)式輸出策略,每次都發(fā)送這些新增的頭部可能會(huì)造成浪費(fèi)。
與往常一樣,這個(gè)功能也可以通過(guò) HTTP 響應(yīng)頭和 meta 標(biāo)簽兩種方式開(kāi)啟并配置:
Accept-CH: DPR, Width, Viewport-Width
或:
<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width">
在啟用了 HTTP Client Hints 的頁(yè)面中,所有子資源請(qǐng)求(無(wú)論什么類(lèi)型,無(wú)論什么方式創(chuàng)建),都會(huì)攜帶 Accept-CH 屬性中所指明的頭部,例如:
BASHAccept: image/webp,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,en-US;q=0.4,ja;q=0.2,de;q=0.2,zh-TW;q=0.2,cs;q=0.2,pt;q=0.2,ko;q=0.2 Connection: keep-alive DPR: 2 Host: qgy18.imququ.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.13 Safari/537.36 Viewport-Width: 1280 Width: 128
有了這些頭部,圖片服務(wù)器可以知道客戶端的 devicePixelRatio 是 2、圖片寬度是 128px、支持 Webp 格式,所以輸出 256px 的雙倍 Webp 圖最合適。但是瀏覽器怎么知道這個(gè)圖片需要作為雙倍圖來(lái)使用呢(也就是說(shuō)還是顯示為 128px)?這就需要在響應(yīng)頭中增加下面這個(gè)字段作為 DPR 的回應(yīng):
Content-DPR: 2
需要注意的是,請(qǐng)求頭中的 Width 字段,是根據(jù) img 標(biāo)簽上的 sizes 屬性算出來(lái)的。如果圖片沒(méi)有指定 sizes,或者圖片請(qǐng)求是通過(guò) JS 創(chuàng)建的,瀏覽器無(wú)法得知 Width,也就不會(huì)攜帶這個(gè)頭部。
實(shí)際上,除了 DPR、Viewport-Width 和 Width 之外,文檔還規(guī)定了兩個(gè)字段,但是經(jīng)過(guò)我的測(cè)試 Chrome 46 并沒(méi)有支持它們,這里簡(jiǎn)單介紹下:
Downlink:用來(lái)指示當(dāng)前網(wǎng)絡(luò)的下行鏈路帶寬,單位是 Mbps;
Save-Data:用來(lái)指示當(dāng)前瀏覽器是否工作在省流模式之下,取值為 1 或 0;
可以看出這兩個(gè)屬性,也是為了盡可能給用戶節(jié)省帶寬而設(shè)計(jì)的??梢灶A(yù)見(jiàn),后續(xù)還會(huì)有更多字段加到 HTTP Client Hints 協(xié)議中來(lái)。隨著 HTTP/2 的普及,頭部壓縮使得增加幾個(gè)頭部字段帶來(lái)的開(kāi)銷(xiāo)變得很小了。
值得注意的是,使用了 HTTP Client Hints 之后,服務(wù)端針對(duì)同一個(gè) URL 可能會(huì)輸出不同的內(nèi)容,所以無(wú)論是中間節(jié)點(diǎn),還是瀏覽器,在實(shí)現(xiàn)響應(yīng) Cache 時(shí)必須小心,需要針對(duì)不同的情況緩存多份內(nèi)容。這需要用到 HTTP/1 中的 Vary 響應(yīng)頭,例如:
Vary: DPR, Width, Downlink
表明如果需要緩存這個(gè)響應(yīng),在生成緩存 Key 的時(shí)候需要將請(qǐng)求頭中的 DPR、Width 和 Downlink 的值計(jì)算進(jìn)去。
好了,HTTP Client Hints 技術(shù)就介紹到這里。很欣慰地看到,大部分 Web 新技術(shù)都是在給 HTML、CSS 和 JavaScript 增加功能和特性,而這項(xiàng)技術(shù)卻是把之前復(fù)雜的代碼和邏輯往后移,讓我們的 HTML 代碼能夠輕裝上陣。一些開(kāi)源圖片處理系統(tǒng)已經(jīng)開(kāi)始支持這個(gè)新特性了,國(guó)外的一些 CDN 托管服務(wù)肯定也在蠢蠢欲動(dòng),我十分期待它的未來(lái)。