How to implement skipWaiting with Create React App

2020-05-23 09:03发布

I would like users to be able to update on the spot when a new service worker is available and waiting? I'm already able to show a pop up when new update is available but I would like to add a button to force update on the spot. I understand this can be achieved with calling skipWaiting but not sure how to implement it with a Create React App. Have anyone able to achieve this? Would appreciate the help. Thank you!

6条回答
【Aperson】
2楼-- · 2020-05-23 09:22

The CRA build\service-worker.js file now (v3 plus) includes code to handle skipWaiting:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

The register function serviceWorker.js file that is called in index.js now accepts a config parameter:

serviceWorker.register({
  onUpdate: registration => {
    const waitingServiceWorker = registration.waiting

    if (waitingServiceWorker) {
      waitingServiceWorker.addEventListener("statechange", event => {
        if (event.target.state === "activated") {
          window.location.reload()
        }
      });
      waitingServiceWorker.postMessage({ type: "SKIP_WAITING" });
    }
  }
});

This will skip waiting and then refresh the page once the update has been applied.

查看更多
做自己的国王
3楼-- · 2020-05-23 09:24

the answer from @user10532439 did not work for me. I ended up using https://github.com/bbhlondon/cra-append-sw and adding

// @ts-ignore
workbox.skipWaiting();

// @ts-ignore
workbox.clientsClaim();

along with

"build": "react-scripts build && cra-append-sw -s ./src/custom-sw.js",
查看更多
时光不老,我们不散
4楼-- · 2020-05-23 09:27

I used a package called https://github.com/bbhlondon/cra-append-sw to append the following code to call trigger skipWaiting:

self.addEventListener('message', event => {
  if (event.data === 'skipWaiting') {
    self.skipWaiting();
  }
});
查看更多
唯我独甜
5楼-- · 2020-05-23 09:35

You should call the skipWaiting method in the service worker's install event. The service worker is decoupled from the UI/React code. So nothing do do with react really.

Right now I have a pattern where I refactored the install event code to its own method, so my service workers tend to look like this:

self.addEventListener( "install", function ( event ) {

    event.waitUntil( installServiceWorker() );

} );

function installServiceWorker() {

    self.skipWaiting();
// do stuff here like pre-caching site core assets
//most likely returning a promise

}

Also: donb't call skipWaiting if you need to sync cached assets with the active UI. If your service worker is being updated and could possibly break the UI then don't call skipWaiting. Instead wait for the user to refresh the page. You could use the messaging infrastructure to inform the user an update is available have them refresh as an example.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2020-05-23 09:47

At serviceWorker.js file can find this code

if (config && config.onUpdate) {
    config.onUpdate(registration);
 }

So implement the config.onUpdate funtion

Create a file swConfig.js

export default {
 onUpdate: registration => {
   registration.unregister().then(() => {
   window.location.reload()
 })
},
onSuccess: registration => {
  console.info('service worker on success state')
  console.log(registration)
 },
}

At index.js send the implement function to serviceWorker

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import swConfig from './swConfig'

ReactDOM.render(<App />, 
document.getElementById('root'));
serviceWorker.register(swConfig);

Check out this repo https://github.com/wgod58/create_react_app_pwaupdate

查看更多
Juvenile、少年°
7楼-- · 2020-05-23 09:49

I know that it has been answered but I found a solution that does not require any additional packages except workbox of course.

at the package.json :

"build": "react-scripts build && node ./src/serviceWorkerBuild"

serviceWorkerBuild.js

const workboxBuild = require('workbox-build');

// NOTE: This should be run *AFTER* all your assets are built
const buildSW = () => {
  // This will return a Promise
  return workboxBuild.injectManifest({
    swSrc: 'src/serviceWorkerRules.js',
    swDest: 'build/sw.js',
    globDirectory: 'build',
    globPatterns: [
      '**\/*.{js,css,html,png}',
    ]
  }).then(({count, size, warnings}) => {
    // Optionally, log any warnings and details.
    warnings.forEach(console.warn);
    console.log(`${count} files will be precached, totaling ${size} bytes.`);
  });
};

buildSW();

and serviceWorkerRules.js where you can add your rules:

importScripts(
    'https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js'
);
/* global workbox */
if (workbox) {
    console.log('Workbox is loaded');

  self.addEventListener('install', event => {
    self.skipWaiting();
  });

    /* injection point for manifest files.  */
    workbox.precaching.precacheAndRoute([]);

/* custom cache rules*/
workbox.routing.registerNavigationRoute('/index.html', {
    blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/],
    });

workbox.routing.registerRoute(
    /\.(?:png|gif|jpg|jpeg|svg)$/,
    workbox.strategies.cacheFirst({
        cacheName: 'images',
        plugins: [
            new workbox.expiration.Plugin({
                maxEntries: 60,
                maxAgeSeconds: 5 * 24 * 60 * 60, // 5 Days
            }),
        ],
    }),
    );

Also add window.location.reload(); at serviceWorker.js at line #78.

查看更多
登录 后发表回答