您當前的位置:首頁 > 娛樂

HTTP1.x 及 Service Worker 快取實踐小結

作者:由 陳星星 發表于 娛樂時間:2018-12-17

在錯縱複雜的網路環境下,如何將頁面快速得傳遞給使用者是前端們的職責,而在此之後,如何減少網路傳輸的花費同樣值得我們關注。本文以 HTTP/1。x 和 Service Worker 快取兩個方面,就如何減少網路傳輸成本為目標,探討下筆者最近對於快取的實踐,權當拋磚引玉

HTTP 快取

The performance of web sites and applications can be significantly improved by reusing previously fetched resources。 Web caches reduce latency and network traffic and thus lessen the time needed to display a representation of a resource。 By making use of HTTP caching, Web sites become more responsive。

根據 MDN 定義可知道,快取是對已獲取資源的重新利用,是提升 WEB 效能的重要指標。根據是否和 Server 進行互動,HTTP 快取分為兩類:

強制快取

協商快取

強制快取是無需和 Server 進行互動,直接在 Client 進行快取。 而協商快取需要和 Server 互動來判斷是否重用快取。

HTTP 快取首部有以下幾種:

Expires

Cache-Control

ETag/If-None-Match

Last-Modified/If-Modified-Since

Expires

語法:

Expires:

Expires

透過設定一個時間戳,控制快取的過期時間點。但缺點是客戶端時間和伺服器時間可能不一致,無法保證快取的同步性。

此外,如果存在

Cache-Control

首部並設定了

max-age

指令,

Expires

首部將被忽略。

Cache-Control

語法:`Cache-Control: [public | private | no-cache | only-if-cached],max-age=|s-maxage=|max-stale[=]|min-fresh=][,must-revalidate|proxy-revalidate|immutable][,no-store|no-transform]

具體配置細節見 MDN,屬於強制快取,不再贅述。這裡只講下自己實踐所用到的設定項。

public | private

max-age=

no-cache | no-store | must-revalidate

public

private

定義了快取的共享性,分為共享(public)與私有(private)快取。共享快取儲存的響應能夠被多個使用者使用,私有快取只能用於單獨使用者。 共享快取可存在於 ISP、閘道器或 CDN 的節點上,能很大程度快取熱門資源,減少網路擁堵與延遲,但存在中間人攻擊的風險,故存在

private

快取 —— 只快取在使用者的瀏覽器端,不會被共享。可根據自己的業務需求,選擇是私有還是共享的。

max-age=

規定了快取時長,以秒為單位。從開始接收到資源為時間點,在接下來的

max-age

時間內使用快取。理論上來說可以長期快取,但帶來的問題是瀏覽器快取的臃腫,根據 RFC2616 最長時常設為一年較為合適,即

Cache-Control: max-age=31536000

no-cache

no-store

must-revalidate

no-cache

規定使用快取之前時一定要經過驗證,比如驗證

ETag/ Last-Modified

等;

no-store

直接禁止瀏覽器以及所有中間快取儲存任何版本的返回響應,每次使用者請求該資產時,都會向伺服器傳送請求,並下載完整的響應;

must-revalidate

快取必須在使用之前驗證舊資源的狀態,並且不可使用過期資源。

ETag/If-None-Match

ETag

是對資源的一個特殊標誌符,能唯一確定資源。語法:

ETag

W/]“

W/

表明了資源是否採用弱型別驗證器進行比較,其較為容易生成但不利於比較。

是對資源的唯一標誌符,其值是一串 ASCII 字串。生成規則沒有一定的要求,但常採用的生成演算法是內容的 hash 值加上內容的最後修改時間。

當響應頭部包含

ETag

時,下次請求時瀏覽器會自動帶上

If-None-Match:

首部,用來驗證資源是否過期。 如果已過期,則以

HTTP 200

返回新的內容響應並帶上新的

ETag

。如果資源未過期,則返回

HTTP 304

告知瀏覽器資源未過期可以繼續使用。

Last-Modified/If-Modified-Since

語法:

Last-Modified: GMT

顧名思義,

Last-Modified/If-Modified-Since

是根據內容最後的修改時間來判斷是否採用快取的方法。但由於最小時間單位為秒,對於要求時間比較精細的資源可能不太適用。

快取優先順序

HTTP/1。x 快取首部的優先順序:

Cache-Control

>

Expires

>

ETag/If-None-Match

>

Last-Modified/If-Modified-Since

, 即在同時設定了上述首部時

Cache-Control

最高,可根據業務需求設定。

以上,便是 HTTP/1。x 快取設定的首部解釋,可以透過Browser Caching Checker 對瀏覽器快取進行檢查。

HTTP1.x 及 Service Worker 快取實踐小結

Server Worker 快取

當下時間點,Service Worker 在瀏覽器上的支援度已高達 86。16%, 所以是時候考慮開啟 Service Worker 來加速你的網站了。不僅可以利用 Service Worker 所帶來的快取好處,還能很容易遷移到 PWA,更大程度發掘 Web App 的能力。

不同於 HTTP 快取,Server Worker 不僅能動態快取資源,而且還能提供 offline 模式,對弱網路環境的使用者極為友好。開啟 Service Worker 大概需要註冊、安裝、快取資源、更新和登出等過程。

HTTP1.x 及 Service Worker 快取實踐小結

接下來以一個小 Demo 為例,簡單介紹如何開啟一個 Service Worker 服務。原始碼見 sw-cache-example

註冊

註冊流程很簡單,只需要判斷瀏覽器是否支援 Service Worker 特性,並在頁面 Load 之後,註冊 Service Worker 服務,關鍵程式碼:

// sw-reg。js

if

‘serviceWorker’

in

navigator

{

window

addEventListener

‘load’

function

()

{

navigator

serviceWorker

register

‘。/sw。js’

)。

then

function

registration

{

// Registration was successful

console

log

‘ServiceWorker registration successful with scope: ’

registration

scope

},

function

err

{

// registration failed :(

console

log

‘ServiceWorker registration failed: ’

err

}

})

}

安裝

安裝過程需要做的有:監聽

install

事件,並在其回撥事件內快取資源。

var

CACHE_NAME

=

‘cache-v1’

var

urlsToCache

=

‘/’

‘/styles/main。css’

‘/script/main。js’

self

addEventListener

‘install’

function

event

{

// Perform install steps

event

waitUntil

caches

open

CACHE_NAME

)。

then

function

cache

{

console

log

‘Opened cache’

return

cache

addAll

urlsToCache

})

})

響應快取

最重要的一步,就是在資源被快取後利用快取了。需要做的也很簡單:監聽

fetch

事件 -> 對已快取的資源進行響應。

self

addEventListener

‘fetch’

function

event

{

event

respondWith

caches

match

event

request

)。

then

function

response

{

if

response

{

return

response

}

return

fetch

event

request

})

})

更新

更新也是 Service Worker 很重要的一步,其過程也很易懂:驗證資源是否過期 -> 對過期的資源進行刪除並快取新的資源。

self

addEventListener

‘activate’

function

event

{

var

cacheWhitelist

=

‘pages-cache-v1’

‘blog-posts-cache-v1’

event

waitUntil

caches

keys

()。

then

function

cacheNames

{

return

Promise

all

cacheNames

map

function

cacheName

{

if

cacheWhitelist

indexOf

cacheName

===

-

1

{

return

caches

delete

cacheName

}

})

})

})

登出

登出只需要拿到 Service Worker 例項,呼叫

unregister

即可。

if

‘serviceWorker’

in

navigator

{

navigator

serviceWorker

ready

then

registration

=>

{

registration

unregister

()

})

}

至此,基本完成了 Service Worker 的基本部署,開啟其提供的快取能力。

實踐過程中遇到的坑

遷移 HTTP 請求方法為

fetch

由於在響應快取時,需要透過監聽

fetch

事件來響應快取,故需要更改 HTTP 請求方法為

fetch

,其 API 參見 MDN。 對於不支援

fetch

的瀏覽器,可以使用這個 fetch 進行打補丁。

2。 取消

fetch

請求

由於

fetch

沒有提供原生的取消方法,故需要使用 signal 來取消

fetch

請求。

const

controller

=

new

AbortController

()

const

signal

=

controller

signal

fetch

‘/some/url’

{

signal

})

then

res

=>

res

json

())

then

data

=>

{

// do something with “data”

})

catch

err

=>

{

if

err

name

==

‘AbortError’

{

return

}

})

// controller。abort(); // can be called at any time

Polyfill 參照 abortcontroller-polyfil

3。增加 Service Worker 開關

Service Worker 提供的快取雖然好用,但有時候需要根據業務登出 Service Worker, 這時就需要一個開關來控制。而且應該在第一次部署的時候就增加開關,對於快取進行控制。

fetch

API

switch

then

res

=>

{

const

isOn

=

res

status

if

isOn

{

sw

register

()

}

else

{

sw

unregister

()

}

})

catch

err

=>

{

console

error

‘fetch sw status error’

err

})

4。 入口檔案取消快取 對於一般的 SPA,是透過入口檔案進行資源的索引,所以對入口檔案應該不予快取,並要求其強制更新。在使用sw-precache-webpack-plugin應排除入口檔案:

new

SWPrecacheWebpackPlugin

{

cacheId

‘my-project-name’

dontCacheBustUrlsMatching

/\。\w{8}\。/

filename

‘service-worker。js’

minify

true

navigateFallback

PUBLIC_PATH

+

‘index。html’

staticFileGlobsIgnorePatterns

/\。map$/

/asset-manifest\。json$/

/index\。html$/

],

}

),

對入口檔案可以設定 HTTP 響應首部:

Cache-Control

no

-

cache, no-store, must-revalidate

其含義是不使用本地及任何中間儲存快取,必須和伺服器取得驗證才能拿到新的內容。

5。 如果不想自己編寫 Service Worker, 可以參照網上的模板或外掛 :

create-react-app and sw-precache-webpack-plugin

workbox

總結

使用

Cache-Control

對靜態資源進行長期快取,配合 webpack 打包生成的檔案 hash 名,可全部採用這一策略

使用

ETag/If-None-Match

對內容 hash 進行精確快取

對於時間要求不精確的資源,使用

Last-Modified/If-Modified-Since

對修改時間對內容進行快取,以替代使用

ETag/If-None-Match

對 CPU 的高消耗

使用

Service Worker

提供動態快取和離線能力

所以,現在開始開啟除錯工具,為你的網站增加快取吧~ ✌️

Reference

HTTP Caching

Browser Caching Checker

Understanding The Vary Header

ServiceWorker Cache

標簽: 快取  worker  service  http  modified