在未預先快取的情況下使用 Workbox

到目前為止,本說明文件主要著重於預先快取,經常提到 generateSWinjectManifest 建構工具。雖然在服務工作者中加入預先快取邏輯有許多好處,但您不一定要使用預先快取才能使用 Workbox。

也許您的專案只需要執行階段快取,或是您想要以更簡潔的方式整合 Service Worker API,例如網路推送。這些是您不想使用 Workbox 建構工具的情況,也是本文要介紹的內容。

使用 bundler 時

在網頁開發領域中,套件組合器相當重要,因此您的專案很可能會使用套件組合器。在這種情況下,請務必注意,如果您沒有預先快取任何內容,就不需要使用 bundler 外掛程式 (例如 workbox-webpack-plugin)。系統會將您的服務工作處理程序視為應用程式中的獨立進入點。

您將在專案來源目錄的根目錄中建立服務工作者,並使用應用程式所需的任何 Workbox 模組。以下範例不使用預先快取,而是在個別 Cache 例項中,為導覽和圖片資產要求設定快取策略:

// sw.js
import {NetworkFirst, CacheFirst} from 'workbox-strategies';
import {registerRoute, NavigationRoute, Route} from 'workbox-routing';

const navigationRoute = new NavigationRoute(new NetworkFirst({
  cacheName: 'navigations'
}));

const imageAssetRoute = new Route(({request}) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'image-assets'
}));

registerRoute(navigationRoute);
registerRoute(imageAssetRoute);

接著,您只需在所選的 bundler 中,將此服務工作站指定為進入點即可。以下列舉幾個範例,說明如何在幾個熱門的 bundler 中執行這項操作。

webpack

webpack 會在其 entry 設定中接受進入點。使用這項方法時,請注意下列幾點:

  1. 為確保服務工作站擁有盡可能廣泛的範圍,您應該將其輸出至輸出目錄的根目錄。
  2. 您不應為服務工作者建立版本,因為更新服務工作者會產生新的雜湊,這可能會導致網站上部署多個服務工作者。

為滿足上述條件,您可以將函式傳送至 output.filename,以檢查目前處理的進入點是否為 Service Worker 進入點。否則,版本控制檔案會寫入正常目的地。

// webpack.config.js
import process from 'process';

const isProd = process.env.NODE_ENV === 'production';

export default {
  mode: isProd ? 'production' : 'development',
  context: process.cwd(),
  entry: {
    // Service worker entry point:
    sw: './src/sw.js',
    // Application entry point:
    app: './src/index.js'
  },
  output: {
    filename: ({runtime}) => {
      // Check if the current filename is for the service worker:
      if (runtime === 'sw') {
        // Output a service worker in the root of the dist directory
        // Also, ensure the output file name doesn't have a hash in it
        return '[name].js';
      }

      // Otherwise, output files as normal
      return 'js/[name].[contenthash:8].js';
    },
    path: './dist',
    publicPath: '/',
    clean: true
  }
};

匯總

Rollup 與 webpack 的情況類似,只是多個進入點會指定為在陣列中匯出的個別設定物件:

// rollup.config.js
import { nodeResolve } from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';

// Plugins common to both entry points
const plugins = [
  nodeResolve(),
];

export default [
  // Application entry point
  {
    input: './src/index.js',
    output: {
      dir: './dist/js',
      format: 'esm'
    },
    plugins
  },
  // Service worker entry point
  {
    input: './src/sw.js',
    output: {
      file: './dist/sw.js',
      format: 'iife'
    },
    plugins: [
      ...plugins,
      // This @rollup/plugin-replace instance replaces process.env.NODE_ENV
      // statements in the Workbox libraries to match your current environment.
      // This changes whether logging is enabled ('development') or disabled ('production').
      replace({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
      })
    ]
  }
];

esbuild

esbuild 提供簡單直接的指令列介面:

npx esbuild ./src/sw.js --bundle --minify --outfile=./dist/sw.js

根據預設,esbuild 會將 process.env.NODE_ENV 替換為「development」,如果啟用精簡功能,則會替換為「production」。

沒有使用 workbox-sw 的套裝組合

您的專案甚至可能沒有套裝組合。如果您使用 importScripts 匯入 Workbox 執行階段,workbox-sw 就可以從服務工作站中的 CDN 為您載入 Workbox 執行階段,您無須進行建構步驟:

// sw.js

// Imports Workbox from the CDN. Note that "6.2.0" of the URL
// is the version of the Workbox runtime.
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');

const navigationRoute = new workbox.routing.NavigationRoute(new workbox.strategies.NetworkFirst({
  cacheName: 'navigations'
}));

const imageAssetRoute = new workbox.routing.Route(({request}) => {
  return request.destination === 'image';
}, new workbox.strategies.CacheFirst({
  cacheName: 'image-assets'
}));

workbox.routing.registerRoute(navigationRoute);
workbox.routing.registerRoute(staticAssetRoute);

如果從 CDN 載入 Workbox 執行階段的可能性不大,您可以使用 workbox-sw 搭配本機網址

結論

現在您已瞭解如何在不預先快取的情況下使用 Workbox,不再與特定組合工具或建構工具建立連結了。這樣一來,您就能靈活運用 Workbox 的執行階段快取程式碼,只使用您感興趣的部分來手動製作服務工作站。