Atualizando seu PWA em produção

Você já ouviu a piada de que, se instalou o ServiceWorker, é hora de mudar o domínio? Agora direi qual é o seu significado e o que fazer se, mesmo assim, decidir que precisa de um PWA.





Nas instruções para o tipo deste ou deste ServiceWorker e trabalhando com ele, quase nenhuma atenção é dada. E tenho certeza de que artigos como este são a primeira coisa que você lê antes de usar. Mas no momento em que, após tais artigos, seu PWA recém-assado finalmente apareceu em produção e os usuários tiveram a oportunidade de adicionar outro ícone à sua área de trabalho, saiba que você passou do ponto sem volta.





Com sua permissão, não irei me alongar na descrição do Service Worker (doravante SW) e como ele funciona. Habré já tem um bom artigo sobre isso . Nem importa qual SW você tem especificamente. Talvez você esteja usando criar-reagir-app , o que significa que a biblioteca Workbox é responsável por SW para você . Talvez você mesmo tenha implementado o SW, com alguma estratégia de cache complicada. A pilha não é realmente importante. A mesma documentação do CRA diz que tudo que você precisa é mudar uma linha e obter todas as delícias de um comportamento semelhante ao de um aplicativo. Você escreveu .register()



e espera o resultado. E você vai conseguir.





Na próxima vez que um cliente insatisfeito lhe pedir para mudar a cor desse botão laranja ou finalmente resolver o bug do foco voador, você se verá em uma situação incrível. Há um hotfix no repositório, o contêiner foi montado e o nginx está distribuindo a versão mais recente com certeza, mas o cliente ainda está insatisfeito por algum motivo. Oh sim, agora somos PWA.





- Atualize a página. Como isso não ajuda? E se CTRL + R?





Então, o que fazer quando a atualização frenética da página não ajuda e o cliente ainda vê o botão laranja falso?





É importante lembrar que o SW tenta se comportar como um aplicativo de desktop.





Vamos lembrar como o aplicativo de desktop se atualiza. Ele baixa o instalador novo, remove a versão antiga e a reinstala. depois disso o usuário recebe uma nova versão do aplicativo.





SW.





SW : installing, waiting active. Active - , SW. installing waiting SW active. installing SW , . waiting , SW ( ). .





SW, , - . SW , . . , . , .





, , "". , SW : , , , . , SW . , .





SW installing, waiting active. - . , .





, , .





№1: SW

( ) - SW. SW skipWaiting()



, . SW . "" .

: , . , skipWaiting()



, , .





№2: SW

, . navigator.serviceWorker



controllerchange



, SW . installing.

skipWaiting()



, . :





navigator.serviceWorker.addEventListener('controllerchange',  ()  => window.location.reload());
      
      



SW , .

, , . , , . .





№3:

, , SW , - - .





controllerchange



, , , .

, SW, ServiceWorkerRegistration



. .register()



, . API . , update()



, SW . , .





(active) SW navigator.serviceWorker.controller



active . (waiting) (installing) SW.





SW postMessage()



, iframe , API. SW . SW.





addEventListener('message', ev => {  
  if (ev.data === 'skipWaiting') return skipWaiting();
});
      
      



Workbox CRA, .





Em seguida, precisamos rastrear a aparência do SW em espera. Na minha opinião, é melhor não reagir todas as vezes ao SW com o status de instalação, como está escrito em alguns guias, mas esperar até que o objeto de registro SW retorne verdadeiro no campo waiting



. Isso tornará a atualização mais lenta, mas não acionará seu modal quando o SW for instalado pela primeira vez.





Depois de esperar pelo SW pendente, chamamos uma janela modal na qual o usuário pode confirmar a atualização. Após a confirmação, ligamos skipWaiting()



e forçadamente recarregamos a página conforme descrito acima. Se ocorrer uma falha, a atualização será adiada. O código no meu caso será semelhante a este:





//   
const askUserToUpdate = reg => {
  return Modal.confirm({
    onOk: async () => {
      //    
      navigator.serviceWorker.addEventListener('controllerchange', () => {
        window.location.reload();
      });

      //   
      if (reg && reg.waiting) {
        reg.waiting.postMessage({ type: 'SKIP_WAITING' });
      }
    },

    onCancel: () => {
      Modal.destroyAll();
    },
    icon: null,
    title: ' ! 
      
      










All Articles