การใช้ Workbox โดยไม่แคชล่วงหน้า

จนถึงปัจจุบัน เอกสารประกอบนี้ค่อนข้างมากด้านการแคชล่วงหน้า โดยมักจะพูดถึงเครื่องมือสร้างเป็นอย่างมากของ generateSW และ injectManifest แม้ว่าจะมีเหตุผลดีๆ มากมายที่ควรใส่ตรรกะการแคชล่วงหน้าไว้ในโปรแกรมทำงานของบริการ แต่คุณไม่จำเป็นต้องใช้การแคชล่วงหน้าเพื่อใช้ Workbox

บางทีโปรเจ็กต์ของคุณอาจต้องการเพียงการแคชรันไทม์ หรือคุณอาจต้องการวิธีที่สะอาดขึ้นในการผสานรวม API ของโปรแกรมทำงานของบริการ เช่น Web Push ต่อไปนี้คือกรณีที่คุณไม่ต้องการใช้เครื่องมือสร้างของ Workbox ซึ่งมีรายละเอียดครอบคลุมอยู่ในบทความนี้

เมื่อใช้ Bundler

Bundler มีความโดดเด่นในด้านการพัฒนาเว็บ และมีโอกาสสูงที่โปรเจ็กต์ของคุณจะใช้ ในกรณีนี้ คุณควรทราบว่าไม่จำเป็นต้องใช้ปลั๊กอิน Bundler (เช่น workbox-webpack-plugin) หากไม่ได้แคชล่วงหน้า คุณจะใช้ Service Worker เป็นจุดแรกเข้าแยกต่างหากในแอปพลิเคชันของคุณ

ในรูทของไดเรกทอรีต้นทางของโปรเจ็กต์ คุณจะต้องสร้าง Service Worker และใช้โมดูล 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);

จากตรงนี้ คุณควรระบุ Service Worker นี้เป็นจุดแรกเข้าใน Bundler ที่คุณเลือก ด้านล่างนี้เป็นตัวอย่างวิธีดำเนินการใน Bundler ยอดนิยมบางส่วน

Webpack

webpack ยอมรับจุดแรกเข้าในการกำหนดค่า entry มี 2-3 สิ่งที่คุณควรทราบเมื่อใช้วิธีนี้

  1. เพื่อให้ Service Worker มีขอบเขตที่กว้างที่สุด คุณจะต้องใช้เอาต์พุตไปที่รูทของไดเรกทอรีเอาต์พุต
  2. คุณไม่ต้องการให้โปรแกรมทำงานของบริการเป็นเวอร์ชัน เนื่องจากการอัปเดตโปรแกรมจะสร้างแฮชใหม่ซึ่งอาจส่งผลให้มีการทำให้โปรแกรมทำงานของบริการหลายรายการใช้งานได้บนเว็บไซต์ของคุณ

เพื่อให้เป็นไปตามเงื่อนไขข้างต้น ระบบจะส่งฟังก์ชันไปยัง output.filename ซึ่งจะตรวจสอบว่าจุดแรกเข้าปัจจุบันที่ประมวลผลคือจุดแรกเข้าของโปรแกรมทำงานของบริการหรือไม่ ไม่เช่นนั้นระบบจะเขียนไฟล์ที่มีเวอร์ชันไปยังปลายทางปกติของไฟล์เหล่านั้น

// 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
  }
};

รายงาน

ภาพรวมเป็นสถานการณ์ที่คล้ายกับ 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 มีอินเทอร์เฟซของบรรทัดคำสั่งที่ตรงไปตรงมา:

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

esbuild จะดูแลการแทนที่ process.env.NODE_ENV ด้วย "การพัฒนา" โดยค่าเริ่มต้น หรือ "เวอร์ชันที่ใช้งานจริง" หากเปิดใช้การลดขนาด

หากไม่มี Bundler ให้ใช้ workbox-sw

โปรเจ็กต์ของคุณอาจไม่ได้ใช้ Bundler workbox-sw สามารถโหลดรันไทม์ของ Workbox ให้คุณจาก CDN ภายใน Service Worker ของคุณโดยไม่ต้องมีขั้นตอนของบิลด์หากนำเข้าด้วย importScripts

// 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-sw กับ URL ในเครื่องได้

บทสรุป

เมื่อทราบวิธีใช้ Workbox โดยไม่ต้องแคชล่วงหน้าแล้ว คุณก็ไม่ต้องผูกมัดกับ Bundler หรือเครื่องมือสร้างที่เฉพาะเจาะจงอีกต่อไปแล้ว ซึ่งช่วยให้คุณมีความยืดหยุ่นในการสร้างสรรค์ Service Worker โดยใช้เพียงโค้ดการแคชรันไทม์ของ Workbox ที่คุณสนใจ