擴充生態系
Service Worker 與 PWA

Service Worker 設定檔

本主題說明 Service Worker 設定檔的各項屬性。

修改設定

ngsw-config.json 這個 JSON 設定檔,指定 Angular Service Worker 應該快取哪些檔案與資料 URL,以及應該如何更新已快取的檔案與資料。 Angular CLI 會在 ng build 期間處理這個設定檔。

所有檔案路徑都必須以 / 開頭,這對應到部署目錄——在 CLI 專案中通常是 dist/<project-name>。

除非另有註解,否則模式使用一種 limited* glob 格式,在內部會轉換成正規表示式:

Glob 格式 詳細說明
** 符合 0 個或多個路徑區段
* 符合 0 個或多個字元(不含 /)
? 恰好符合一個字元(不含 /)
! 前綴 把模式標示為否定,意思是只包含不符合該模式的檔案

特殊字元需要跳脫

請注意,在正規表示式中具有特殊意義的某些字元不會被跳脫,而且在內部的 glob 轉正規表示式過程中,模式也不會被包在 ^/$ 中。

$ 是正規表示式中的特殊字元,符合字串的結尾,在把 glob 模式轉換成正規表示式時不會自動跳脫。

如果你想逐字符合 $ 字元,你必須自行跳脫它(使用 \\$)。例如,glob 模式 /foo/bar/$value 會產生一個無法符合的表示式,因為一個字串在結束之後不可能再有任何字元。

在轉換成正規表示式時,模式不會自動被包在 ^ 與 $ 中。因此,這些模式只會部分符合請求的 URL。

如果你想讓模式符合 URL 的開頭與/或結尾,你可以自行加入 ^/$。例如,glob 模式 /foo/bar/*.js 會同時符合 .js 與 .json 檔案。如果你只想符合 .js 檔案,請使用 /foo/bar/*.js$。

模式範例:

模式 詳細說明
/**/*.html 指定所有 HTML 檔案
/*.html 只指定根目錄中的 HTML 檔案
!/**/*.map 排除所有 sourcemap

Service Worker 設定屬性

以下各節說明設定檔的每一項屬性。

appData

這一節讓你可以傳入任何想傳的資料,用來描述應用程式的這個特定版本。 SwUpdate 服務會把該資料包含在更新通知中。 許多應用程式使用這一節提供額外資訊,用來顯示 UI 彈出視窗,通知使用者有可用更新。

index

指定作為索引頁面來滿足導覽請求的檔案。 通常是 /index.html。

assetGroups

_資產_是應用程式版本的一部分、會隨應用程式一起更新的資源。 它們可以包括從頁面來源載入的資源,以及從 CDN 與其他外部 URL 載入的第三方資源。 由於並非所有這類外部 URL 在建置時都已知,因此可以使用 URL 模式來進行符合比對。

HELPFUL: 若要讓 Service Worker 處理從不同來源載入的資源,請確定每個來源的伺服器都已正確設定 CORS。

這個欄位包含一個資產群組陣列,每個群組都定義一組資產資源,以及快取它們所依據的政策。

{
  "assetGroups": [
    {
      …
    },
    {
      …
    }
  ]
}

HELPFUL: 當 ServiceWorker 處理一個請求時,它會依資產群組在 ngsw-config.json 中出現的順序來檢查。 第一個符合所請求資源的資產群組會處理該請求。

建議把較具體的資產群組放在清單較前面的位置。 例如,符合 /foo.js 的資產群組,應該出現在符合 *.js 的群組之前。

每個資產群組都同時指定一組資源,以及管理這些資源的政策。 這個政策決定資源何時被擷取,以及偵測到變更時會發生什麼事。

資產群組遵循這裡所示的 TypeScript 介面:

interface AssetGroup {
  name: string;
  installMode?: 'prefetch' | 'lazy';
  updateMode?: 'prefetch' | 'lazy';
  resources: {
    files?: string[];
    urls?: string[];
  };
  cacheQueryOptions?: {
    ignoreSearch?: boolean;
  };
}

每個 AssetGroup 由以下資產群組屬性定義。

name

name 是必填的。 它在設定的各版本之間識別這個特定的資產群組。

installMode

installMode 決定這些資源最初如何被快取。 installMode 可以是兩個值之一:

值 詳細說明
prefetch 告訴 Angular Service Worker,在它快取應用程式目前版本的同時,擷取每一個列出的資源。這會大量消耗頻寬,但能確保資源在被請求時隨時可用,即使瀏覽器目前離線也一樣。
lazy 不預先快取任何資源。Angular Service Worker 只會快取它收到請求的資源。這是一種隨選快取模式。從未被請求的資源不會被快取。這適用於不同解析度的圖片這類情境,讓 Service Worker 只快取符合特定螢幕與方向的正確資產。

預設值為 prefetch。

updateMode

對於已經在快取中的資源,updateMode 決定在發現應用程式新版本時的快取行為。 群組中自前一版本以來已變更的任何資源,都會依 updateMode 來更新。

值 詳細說明
prefetch 告訴 Service Worker 立即下載並快取已變更的資源。
lazy 告訴 Service Worker 不要快取這些資源。它會把它們視為未被請求,並等到它們再次被請求時才更新。updateMode 為 lazy 只有在 installMode 也是 lazy 時才有效。

預設值為 installMode 所設定的值。

resources

這一節說明要快取的資源,細分如下列群組:

資源群組 詳細說明
files 列出符合發行目錄中檔案的模式。這些可以是單一檔案,或是符合多個檔案的類 glob 模式。
urls 包括在執行階段進行比對的 URL 與 URL 模式。這些資源不會被直接擷取,也沒有內容雜湊,而是依其 HTTP 標頭來快取。這對 Google Fonts 服務這類 CDN 最有用。
(不支援否定 glob 模式,且 ? 會逐字比對;也就是說,它不會符合 ? 以外的任何字元。)

cacheQueryOptions

這些選項用來修改請求的比對行為。 它們會傳給瀏覽器的 Cache#match 函式。 詳情請參閱 MDN。 目前只支援以下選項:

選項 詳細說明
ignoreSearch 忽略查詢參數。預設值為 false。

dataGroups

與資產資源不同,資料請求不會隨應用程式一起版控。 它們會依手動設定的政策來快取,這類政策對 API 請求與其他資料相依性等情境更有用。

這個欄位包含一個資料群組陣列,每個群組都定義一組資料資源,以及快取它們所依據的政策。

{
  "dataGroups": [
    {
      …
    },
    {
      …
    }
  ]
}

HELPFUL: 當 ServiceWorker 處理一個請求時,它會依資料群組在 ngsw-config.json 中出現的順序來檢查。 第一個符合所請求資源的資料群組會處理該請求。

建議把較具體的資料群組放在清單較前面的位置。 例如,符合 /api/foo.json 的資料群組,應該出現在符合 /api/*.json 的群組之前。

資料群組遵循這個 TypeScript 介面:

export interface DataGroup {
  name: string;
  urls: string[];
  version?: number;
  cacheConfig: {
    maxSize: number;
    maxAge: string;
    timeout?: string;
    refreshAhead?: string;
    strategy?: 'freshness' | 'performance';
  };
  cacheQueryOptions?: {
    ignoreSearch?: boolean;
  };
}

每個 DataGroup 由以下資料群組屬性定義。

name

與 assetGroups 類似,每個資料群組都有一個 name,用來唯一識別它。

urls

URL 模式的清單。 符合這些模式的 URL 會依這個資料群組的政策來快取。 只有非變更型的請求(GET 與 HEAD)會被快取。

  • 不支援否定 glob 模式
  • ? 會逐字比對;也就是說,它_只_符合 ? 字元

version

API 有時會以不向後相容的方式變更格式。 應用程式的新版本可能與舊的 API 格式不相容,因此也可能與來自該 API 的既有快取資源不相容。

version 提供一種機制,指出正在快取的資源已以不向後相容的方式更新,而舊的快取項目——也就是來自先前版本的項目——應該被捨棄。

version 是一個整數欄位,預設值為 1。

cacheConfig

以下屬性定義符合的請求被快取所依據的政策。

maxSize

快取中項目或回應的最大數量。

CRITICAL: 無上限的快取可能以無限制的方式增長,最終超過儲存配額,導致快取被清除。

maxAge

maxAge 參數指出回應在被視為無效並清除之前,可以在快取中保留多久。maxAge 是一個持續時間字串,使用以下單位後綴:

後綴 詳細說明
d 天
h 小時
m 分鐘
s 秒
u 毫秒

例如,字串 3d12h 表示把內容快取最多三天半。

timeout

這個持續時間字串指定網路逾時。 網路逾時是指在設定如此做的情況下,Angular Service Worker 在改用已快取回應之前,等待網路回應的時間。 timeout 是一個持續時間字串,使用以下單位後綴:

後綴 詳細說明
d 天
h 小時
m 分鐘
s 秒
u 毫秒

例如,字串 5s30u 相當於五秒又 30 毫秒的網路逾時。

refreshAhead

這個持續時間字串指定在已快取資源到期之前的多久,Angular Service Worker 應該主動嘗試從網路重新整理該資源。 refreshAhead 持續時間是一個可選設定,決定 Service Worker 應該在已快取回應到期前多久,從網路發起請求來重新整理該資源。

後綴 詳細說明
d 天
h 小時
m 分鐘
s 秒
u 毫秒

例如,字串 1h30m 相當於到期時間前一小時又 30 分鐘。

strategy

Angular Service Worker 對資料資源可以使用兩種快取策略之一。

快取策略 詳細說明
performance 預設策略,針對儘可能快的回應進行最佳化。如果資源存在於快取中,就使用已快取的版本,不發出網路請求。這會以更好的效能換取一定程度的過時(視 maxAge 而定)。這適用於不常變更的資源;例如,使用者頭像圖片。
freshness 針對資料的即時性進行最佳化,優先從網路擷取所請求的資料。只有在網路逾時(依 timeout 而定)時,請求才會退回使用快取。這適用於經常變更的資源;例如,帳戶餘額。

HELPFUL: 你也可以模擬第三種策略,staleWhileRevalidate,它會在已有快取資料時回傳快取資料,但同時在背景從網路擷取新資料,供下次使用。 若要使用這個策略,請在 cacheConfig 中把 strategy 設為 freshness,把 timeout 設為 0u。

這基本上會執行以下動作:

  1. 先嘗試從網路擷取。
  2. 如果網路請求沒有立即完成,也就是在 0 ms 的逾時之後,忽略快取年齡並退回使用已快取的值。
  3. 網路請求完成後,更新快取供未來請求使用。
  4. 如果資源不存在於快取中,無論如何都等待網路請求。
cacheOpaqueResponses

Angular Service Worker 是否應該快取不透明回應。

如果未指定,預設值取決於資料群組所設定的策略:

策略 詳細說明
使用 freshness 策略的群組 預設值為 true,Service Worker 會快取不透明回應。這些群組每次都會請求資料,只有在離線或網路緩慢時才退回使用已快取的回應。因此,Service Worker 是否快取了一個錯誤回應並不重要。
使用 performance 策略的群組 預設值為 false,Service Worker 不會快取不透明回應。這些群組會持續回傳已快取的回應,直到 maxAge 到期為止,即使錯誤是因為暫時的網路或伺服器問題也一樣。因此,Service Worker 若快取了錯誤回應,就會造成問題。

關於不透明回應的說明

如果你不熟悉,不透明回應是一種特殊類型的回應,在請求位於不同來源、且該來源不回傳 CORS 標頭的資源時回傳。 不透明回應的特性之一是,Service Worker 不被允許讀取它的狀態,意思是它無法檢查請求是否成功。 更多詳細資訊,請參閱 fetch() 簡介。

如果你無法實作 CORS——例如,你無法控制該來源——對於會產生不透明回應的資源,請優先使用 freshness 策略。

cacheQueryOptions

詳情請參閱 assetGroups。

這個可選的區段讓你可以指定一份自訂的 URL 清單,這些 URL 會重新導向到索引檔案。

處理導覽請求

ServiceWorker 會把不符合任何 asset 或 data 群組的導覽請求,重新導向到指定的索引檔案。 符合以下條件的請求會被視為導覽請求:

  • 它的方法是 GET
  • 它的模式是 navigate
  • 依 Accept 標頭的值判斷,它接受 text/html 回應
  • 它的 URL 符合以下條件:
    • URL 的最後一個路徑區段不能包含副檔名(也就是 .)
    • URL 不能包含 __

HELPFUL: 若要設定導覽請求是否要送往網路,請參閱 navigationRequestStrategy 一節與 applicationMaxAge 一節。

比對導覽請求 URL

雖然這些預設條件在大多數情況下都適用,但有時你會想設定不同的規則。 例如,你可能會想忽略特定的路由,例如不屬於 Angular 應用程式的路由,並把它們傳給伺服器。

這個欄位包含一個 URL 陣列,以及在執行階段比對的類 glob URL 模式。 它可以同時包含否定模式(也就是以 ! 開頭的模式)與非否定模式和 URL。

只有 URL 符合_任何_非否定 URL/模式、且_不_符合任何否定模式的請求,才會被視為導覽請求。 比對時會忽略 URL 查詢。

如果省略這個欄位,它的預設值為:

[
  '/**', // Include all URLs.
  '!/**/*.*', // Exclude URLs to files (containing a file extension in the last segment).
  '!/**/*__*', // Exclude URLs containing `__` in the last segment.
  '!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
];

這個可選屬性讓你可以設定 Service Worker 如何處理導覽請求:

{
  "navigationRequestStrategy": "freshness"
}
可能的值 詳細說明
'performance' 預設設定。提供指定的索引檔案,該檔案通常已快取。
'freshness' 把請求傳給網路,在離線時退回 performance 行為。這個值在伺服器使用 3xx HTTP 重新導向狀態碼把導覽請求導向別處時很有用。使用這個值的原因包括:
  • 當驗證不是由應用程式處理時,重新導向到驗證網站
  • 在網站改版後,重新導向特定的 URL,以避免既有的連結/書籤失效
  • 當頁面暫時無法使用時,重新導向到不同的網站,例如伺服器狀態頁面

IMPORTANT: freshness 策略通常會導致更多請求送往伺服器,這可能會增加回應延遲。建議只要有可能,就使用預設的效能策略。

applicationMaxAge

這個可選屬性讓你可以設定 Service Worker 從快取提供某個應用程式版本多久。這個期限是從你建置應用程式的時間起算,而不是從檔案快取到裝置上的時間起算。

在 applicationMaxAge 之內,Service Worker 會從快取提供檔案。超過之後,Service Worker 會把所有請求都送往網路,包括資產與資料請求,即使沒有較新的版本也一樣。在部署新版建置、且裝置已取得它之前,應用程式無法離線運作。

若你省略 applicationMaxAge,就沒有限期上限,Service Worker 會提供快取的版本,不論它們有多舊。