React w/ Service Worker & Push Notifications

后端 未结 1 816
北海茫月
北海茫月 2021-02-08 16:55

Some initial considerations:

\"react\": \"^16.8.2\",
\"react-scripts\": \"2.1.5\"

I have created a new react app and I need to implement Push N

相关标签:
1条回答
  • 2021-02-08 17:46

    Customizing your service worker with Create React App is possible, but could be quite difficult and hacky.

    Out of the box, CRA uses Workbox GenerateSW webpack plugin to generate service-worker.js file, and you cannot inject any code to it (you could with CRA@1, not any more with since CRA@2)

    You have several strategies, I'll start with the simplest one.

    Solution 1: provide your own service-worker file

    • in src/index.js enable service worker:
      // serviceWorker.unregister()
      serviceWorker.register()
      
    • in src/serviceWorker.js register your custom file:

      // if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
      if ('serviceWorker' in navigator) {
      
      // const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
      const swUrl = `${process.env.PUBLIC_URL}/custom-service-worker.js`;
      

      You have to change the name cause when running dev server, CRA provides a mock for service-worker.js

    • in public/ folder, create custom-service-worker.js file. Webpack will copy it as is in the build/ folder

    Pros: quick, dirty win

    Cons: your custom file is not processed with Webpack (no imports), and you must implement the network caching logic by yourself (assuming you want a PWA) since you're bypassing Workbox plugins

    Solution 2: append your code to generated service-worker

    There's a module for it: cra-append-sw. You're in charge to provide the appended code.

    Pros: easy setup, takes advantages GenerateSW

    Cons: appended code is processed with Babel/Webpack, but not using CRA's config (you could opt-out). Still use GenerateSW which handle network caching for you. Not sure it works when developing locally

    Solution 3: use Workbox in custom service-worker file

    • apply the first 2 steps of solution #1: change src/index.js and src/serviceWorker.js

    • in src/ folder, create custom-service-worker.js file. It will be processed by Webpack, so you can use ES2016/TypeScript syntax and import modules

      /* eslint no-restricted-globals: "off" */
      import * as precaching from 'workbox-precaching'
      // your own imports
      
      if (self.__precacheManifest) {
      precaching.precacheAndRoute(self.__precacheManifest)
      }
      
      // your own code
      
    • install react-app-rewire:

      • npm add --save-dev react-app-rewired
      • in package.json, in "scripts", replace react-scripts with react-app-rewired
    • tweak webpack configuration: create config-overrides.js in root folder:

      const WebpackBeforeBuildPlugin = require('before-build-webpack')
      const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
      const path = require('path')
      const merge = require('lodash.merge')
      const fs = require('fs')
      
      // from https://www.viget.com/articles/run-multiple-webpack-configs-sequentially/
      class WaitPlugin extends WebpackBeforeBuildPlugin {
      constructor(file, interval = 100, timeout = 60e3) {
       super(function(stats, callback) {
         const start = Date.now()
      
         function poll() {
           if (fs.existsSync(file)) {
             callback()
           } else if (Date.now() - start > timeout) {
             throw Error(`Couldn't access ${file} within ${timeout}s`)
           } else {
             setTimeout(poll, interval)
           }
         }
         poll()
       })
      }
      }
      
      const swOutputName = 'custom-service-worker.js'
      const workerSource = path.resolve(__dirname, 'src', swOutputName)
      
      module.exports = {
      webpack: (config, env) => {
       // we need 2 webpack configurations:
       // 1- for the service worker file.
       //    it needs to be processed by webpack (to include 3rd party modules), and the output must be a
       //    plain, single file, not injected in the HTML page
       const swConfig = merge({}, config, {
         name: 'service worker',
         entry: workerSource,
         output: {
           filename: swOutputName
         },
         optimization: {
           splitChunks: false,
           runtimeChunk: false
         }
       })
       delete swConfig.plugins
      
       // 2- for the main application.
       //    we'll reuse configuration from create-react-app, without a specific Workbox configuration,
       //    so it could inject workbox-precache module and the computed manifest into the BUILT service-worker.js file.
       //    this require to WAIT for the first configuration to be finished
       if (env === 'production') {
         const builtWorkerPath = path.resolve(config.output.path, swOutputName)
         config.name = 'main-application'
         config.plugins.push(
           new WorkboxWebpackPlugin.InjectManifest({
             swSrc: builtWorkerPath,
             swDest: swOutputName
           }),
           new WaitPlugin(builtWorkerPath)
         )
       }
      
       // remove Workbox service-worker.js generator
       const removed = config.plugins.findIndex(
         ({ constructor: { name } }) => name === 'GenerateSW'
       )
       if (removed !== -1) {
         config.plugins.splice(removed, 1)
       } 
      
       const result = [swConfig, config]
       // compatibility hack for CRA's build script to support multiple configurations
       // https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/build.js#L119
       result.output = { publicPath: config.output.publicPath }
       return result
      }
      }
      

    Pros: you can use ES2016/TypeScript code in service-worker file. You still benefit from Workbox network caching facilities, with total control on it

    Cons: complicated and fragile, because of the multiple configuration hack.

    I've used the last solution, cause I needed both caching code from Workbox and some import in my service worker file.

    react-app-rewire-workbox may help simplifying the Webpack configuration (the one for main app). To be tested.

    0 讨论(0)
提交回复
热议问题