Como usar o Workbox sem pré-armazenamento em cache

Até agora, esta documentação tem se concentrado na pré-cache, muitas vezes abordando as ferramentas de build generateSW e injectManifest. Embora haja muitos bons motivos para incluir a lógica de pré-cache no seu worker de serviço, não é necessário usar o pré-cache para usar o Workbox.

Talvez seu projeto precise apenas de armazenamento em cache no tempo de execução ou talvez você queira uma maneira mais limpa de integrar APIs de worker de serviço, como o push da Web. Esses são os casos em que você não quer usar as ferramentas de build do Workbox, e é isso que é abordado neste artigo.

Ao usar um bundler

Os bundlers são proeminentes no cenário de desenvolvimento da Web, e há uma boa chance de seu projeto usar um. Nesse caso, é importante saber que você não precisa usar um plug-in de bundler (como workbox-webpack-plugin) se não estiver pré-cacheando nada. Você vai tratar o worker de serviço como um ponto de entrada separado no seu aplicativo.

Na raiz do diretório de origem do projeto, você vai criar um worker de serviço e usar os módulos do Workbox necessários para o aplicativo. Confira um exemplo sem pré-cache, que configura estratégias de armazenamento em cache para solicitações de ativos de navegação e imagem em instâncias Cache separadas:

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

A partir daí, basta especificar esse worker como um ponto de entrada no bundler de sua escolha. Confira abaixo alguns exemplos de como fazer isso em alguns bundlers conhecidos.

webpack

O webpack aceita pontos de entrada na configuração entry. Há algumas coisas que você precisa saber ao usar essa abordagem:

  1. Para garantir que o service worker tenha o escopo mais amplo possível, ele precisa ser gerado na raiz do diretório de saída.
  2. Não é recomendável que o service worker tenha uma versão, porque as atualizações geram novos hashes que podem resultar na implantação de vários service workers no seu site.

Para atender às condições acima, uma função pode ser transmitida para output.filename, que examina se o ponto de entrada atual que está sendo processado é o ponto de entrada do service worker. Caso contrário, os arquivos com versões são gravados nos destinos normais.

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

consolidação

O rollup é uma situação semelhante ao webpack, exceto que vários pontos de entrada são especificados como objetos de configuração separados exportados em uma matriz:

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

O esbuild oferece uma interface de linha de comando simples:

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

O esbuild vai cuidar da substituição de process.env.NODE_ENV por "development" por padrão ou "production" se a minificação estiver ativada.

Sem um bundler usando workbox-sw

Seu projeto pode nem mesmo usar um bundler. O workbox-sw pode carregar o ambiente de execução do Workbox para você de um CDN no seu worker de serviço e sem uma etapa de build se você o importar com 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);

Se a perspectiva de carregar o Workbox Runtime de um CDN não parecer boa, é possível usar workbox-sw com URLs locais.

Conclusão

Agora que você sabe como usar o Workbox sem pré-cache, não precisa mais se limitar a uma ferramenta de build ou de agrupamento específica. Isso dá a flexibilidade de criar um service worker manualmente usando apenas os bits do código de armazenamento em cache do Workbox que você quer.