Contexto Service Worker

  • Tipo: object
  • Origem: Contexto Nullstack
  • Disponibilidade: server/client
  • readwrite no contexto do server
  • readonly no contexto do client

Ele te dá controle granular do comportamento do seu PWA.

Chaves do worker serão usadas para gerar o arquivo do service worker e devem ser setadas durante o processo de inicialização.

Chaves do worker são congeladas após o processo de inicialização.

As seguintes keys estão disponíveis no objeto durante a inicialização:

  • enabled: boolean
  • preload: string array (relative paths)
  • headers: object

A chave enabled define se o service worker será registrado automaticamente pelo Nullstack.

Por padrão a chave enabled é setada como true no modo de produção e false no modo de desenvolvimento.

O array preload é composto por caminhos que serão cacheados quando o service worker for instalado.

Os assets requeridos para inicializar a aplicação serão pré-carregados automaticamente, e você deverá apenas as páginas extras que você quer que estejam disponíveis em modo offline.

import Nullstack from 'nullstack';
import path from 'path';
import {readdirSync} from 'fs';

class Application extends Nullstack {

  static async start({worker}) {
    const articles = readdirSync(path.join(__dirname, '..', 'articles'));
    worker.preload = [
      ...articles.map((article) => '/' + article.replace('.md', '')),
      '/nullstack.svg',
      '/documentation',
      '/components'
    ]
  }
  
  // ...

}

export default Application;

💡 O exemplo acima foi extraido deste repositório e permite que a documentação esteja totalmente acessível em modo offline.

As seguintes chaves estão disponíveis como readonly no contexto do cliente:

  • enabled: boolean
  • preload: string array (relative paths)
  • online: boolean
  • fetching: boolean
  • responsive: boolean
  • installation: BeforeInstallPromptEvent
  • registration: ServiceWorkerRegistration
  • loading: object

As seguintes chaves estão disponíveis como readwrite no contexto do cliente:

  • headers: object

A chave responsive determina se a aplicação tem todas as respostas necessárias para renderizar a página atual.

O Nullstack irá tentar manter sua aplicação respondendo o maior tempo possível e setará a chave para false somente quando não houver mais alternativas de recuperar qualquer resposta da rede ou offline usando a estratégia de busca para o ambiente.

A chave online irá monitorar os eventos da rede e re-renderizar a aplicação quando o valor de navigator.onLine mudar.

Quando a aplicação voltar a ficar online o Nullstack irá tentar fazer a aplicação responder novamente e re-renderizar se necessário.

import Nullstack from 'nullstack';
// ...

class Application extends Nullstack {
 
  // ...

  render({worker}) {
    if(!worker.responsive) {
      return <OfflineWarning />
    }
    return (
      <main>
        <Home route="/" />
      </main>
    )
  }

}

Você pode acessar a registration e installation do service worker atual pela chave worker para controlar o fluxo do seu PWA.

A chave registration se refere ao registro do service worker e só estará disponível uma vez que o processo de registro esteja completo.

A chave installation se refere a instalação delegada no evento do prompt e apenas estará disponível se o evento beforeinstallprompt ocorrer.

import Nullstack from 'nullstack';

class PWAInstaller extends Nullstack {

  installed = false;
  hidden = false;

  async prompt({worker}) {
    try {
      worker.installation.prompt();
      const {outcome} = await worker.installation.userChoice;
      if (outcome === 'accepted') {
        console.log('User accepted the A2HS prompt');
      } else {
        console.log('User dismissed the A2HS prompt');
      }
    } finally {
      this.hidden = true;
    }
  }
  
  render({worker, project}) {
    if(this.hidden) return false;
    if(!worker.installation) return false;
    return (
      <div>
        <img src={project.favicon} />
        <p> Do you want to add {project.name} to your home screen?</p>
        <button onclick={this.prompt}> Install </button>
        <button onclick={{hidden: true}}> Not Now </button>
      </div>
    )
  }

}

export default PWAInstaller;

Carregando telas

Quando uma função do servidor é chamada o fetching será setado como true até a requisição ser resolvida.

Quando uma função do servidor é chamada a chave com o nome da função do servidor invocada será setada como true na chave loading até a requisição ser resolvida.

Qualquer chave que for chamada no objeto loading sempre irá retornar um valor booleano ao invés de undefined por consistência.

Quando o servidor estiver emulando o contexto do cliente para renderização no lado do servidor, todas as chaves de loading vão sempre retornar false, pulando multiplos ciclos de render por performance.

import Nullstack from 'nullstack';

class Page extends Nullstack {

  static async save() {
    // ...
  }

  async submit() {
    await this.save();
  }
 
  render({worker}) {
    return (
      <form onsubmit={this.save}> 
        {worker.fetching && 
          <span> loading... </span>
        }
        <button disabled={worker.loading.save}> 
          Save
        </button>
      </form>
    )
  }

}

export default Page;

Headers customizadas

Você pode usar a chave headers para configurar as headers que o worker usará na requisição para uma função do servidor

🔥 headers serão ignorados quando uma função do servidor for chamada durante o processo de renderização do lado do servidor

import Nullstack from 'nullstack';

class LoginPage extends Nullstack {

  // ...

  async submit({worker}) {
    // ...
    this.headers['Authorization'] = `Bearer ${token}`;
    // ...
  }

  static async authorize({request}) {
    const authorization = request.headers['Authorization'];
    // ...
  }
  
  // ...

}


export default LoginPage;

✨ Aprenda mais sobre o requisições e respostas do servidor

Estratégia de renderização Server-side

  • Requisições de diferentes origens serão realizadas normalmente;
  • Requisições diferentes do método GET serão realizadas normalmente;
  • Assets com Fingerprints serão carregados no cache no momento da instalação;
  • Assets com Fingerprints serão carregados do cache primeiro, e só então retornados para a rede, se necessário;
  • Caminhos com uma extensão serão recuperados e atualizarão o cache em segundo plano para uma requisição subsequente;
  • Os caminhos navegados serão carregados da rede e se falhar serão retornados do cache para a página na qual o worker.responsive e o worker.online estiverem setados como false;

Estratégia de geração de site estático

  • Requisições de diferentes origens serão realizadas normalmente;
  • Requisições diferentes do método GET serão realizadas normalmente;
  • Assets com Fingerprints serão carregados no cache no momento da instalação;
  • Assets com Fingerprints serão carregados do cache primeiro, e só então retornados para a rede, se necessário;
  • Caminhos com uma extensão serão recuperaados e atualizarão o cache em segundo plano para uma requisição subsequente;
  • A página inicial será carregada na rede primeiro, e se necessário será retornada uma cópia em cache;
  • Caminhos navegados irão carregar apenas os dados da API estática e mesclar com o template da aplicação para gerar a resposta.
  • Navegar para uma rota estática irá fazer cache apenas dos dados daquela página;
  • Quando os dados estiverem indisponíveis no cache ou na rede irá retornar uma página na qual worker.responsive e worker.online estiverem setados como false;

Estratégia customizada

O Nullstack irá instalar automaticamente seu service worker se enabled estiver setado como true com os seguintes eventos:

  • install
  • activate
  • fetch

Você pode sobreescrever qualquer um desses eventos criando um service-worker.js na pasta public;

Se qualquer uma das palavras chaves acima for encontrada o Nullstack injetará sua função no código do service worker ao invés do padrão.

Por conveniência uma chave chamada context é injetada no self do service worker com as seguintes chaves:

function activate(event) {
  event.waitUntil(async function() {
    const cacheNames = await caches.keys();
    const cachesToDelete = cacheNames.filter(cacheName => cacheName !== self.context.environment.key);
    await Promise.all(cachesToDelete.map((cacheName) => caches.delete(cacheName)));
    if (self.registration.navigationPreload) {
      await self.registration.navigationPreload.enable();
    }
    self.clients.claim();
  }());
}

self.addEventListener('activate', activate);

💡 O exemplo acima foi extraido do service worker gerado e usa self.context.enviroment.key

Próximo passo

⚔ Aprenda Como fazer o deploy de uma aplicação Nullstack.