问题
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 Notifications. Following this tutorial, I was able to get up and running in 5 minutes, but now I have to implement the same strategy (kinda) into a react app.
The problem I am facing is that I am able to subscribe to the Notification API, but I'm not sure how to edit the service-worker.js
file to add an event listener to catch the push
event (Handle a Push Event
chapter in the google guide)
回答1:
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, createcustom-service-worker.js
file. Webpack will copy it as is in thebuild/
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
andsrc/serviceWorker.js
in
src/
folder, createcustom-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"
, replacereact-scripts
withreact-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.
来源:https://stackoverflow.com/questions/54770313/react-w-service-worker-push-notifications