React w/ Service Worker 和推送通知
React w/ Service Worker & Push Notifications
一些初步考虑:
"react": "^16.8.2",
"react-scripts": "2.1.5"
我创建了一个新的 React 应用程序,我需要实施推送通知。
按照 this 教程,我能够在 5 分钟内起床并 运行,但现在我必须将相同的策略(有点)实施到 React 应用程序中。
我面临的问题是我能够订阅通知 API,但我不确定如何编辑 service-worker.js
文件以添加事件侦听器以捕获push
事件(google 指南中的 Handle a Push Event
章节)
可以使用 Create React App 自定义您的 Service Worker,但可能会非常困难和棘手。
开箱即用,CRA 将 Workbox GenerateSW webpack plugin to generate service-worker.js
file, and you cannot inject any code to it (you could 与 CRA@1 一起使用,自 CRA@2 起不再使用 Workbox)
你有多种策略,我将从最简单的开始。
解决方案 1:提供您自己的 service-worker 文件
- 在
src/index.js
中启用服务工作者:
// serviceWorker.unregister()
serviceWorker.register()
在src/serviceWorker.js
中注册您的自定义文件:
// 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`;
当 运行 开发服务器时,您必须更改名称,CRA 为 service-worker.js
提供 mock
在public/
文件夹中,创建custom-service-worker.js
文件。 Webpack 会将其 原样 复制到 build/
文件夹
优点:快速、肮脏的胜利
缺点:你的自定义文件没有用Webpack处理(没有导入),你必须自己实现网络缓存逻辑(假设你想要一个 PWA)因为你绕过了 Workbox 插件
解决方案 2:将您的代码附加到生成的 service-worker
它有一个模块:cra-append-sw。
您负责提供附加代码。
优点:设置简单,利用 GenerateSW
缺点:附加代码使用 Babel/Webpack 处理,但不使用 CRA 的配置(您可以选择退出) .仍然使用为您处理网络缓存的 GenerateSW。不确定在本地开发时是否有效
解决方案 3:在自定义 service-worker 文件中使用 Workbox
应用解决方案 #1 的前两个步骤:更改 src/index.js
和 src/serviceWorker.js
在src/
文件夹中,创建custom-service-worker.js
文件。它将被Webpack处理,所以你可以使用ES2016/TypeScript语法并导入模块
/* eslint no-restricted-globals: "off" */
import * as precaching from 'workbox-precaching'
// your own imports
if (self.__precacheManifest) {
precaching.precacheAndRoute(self.__precacheManifest)
}
// your own code
-
npm add --save-dev react-app-rewired
- 在
package.json
中,在"scripts"
中,将react-scripts
替换为react-app-rewired
调整 webpack 配置:在根文件夹中创建 config-overrides.js
:
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
}
}
优点:你可以在 service-worker 文件中使用 ES2016/TypeScript 代码。您仍然受益于 Workbox 网络缓存设施,并对其进行完全控制
缺点:复杂且脆弱,因为有多个配置 hack。
我使用了最后一个解决方案,因为我需要来自 Workbox 的缓存代码和我的 service worker 文件中的一些 import
。
react-app-rewire-workbox 可能有助于简化 Webpack 配置(主应用程序配置)。待测试。
Create React App 4 - 2020 年 10 月发布
Starting with Create React App 4, you have full control over
customizing the logic in this service worker, by creating your own
src/service-worker.js file, or customizing the one added by the
cra-template-pwa (or cra-template-pwa-typescript) template. You can
use additional modules from the Workbox project, add in a push
notification library, or remove some of the default caching logic.
https://create-react-app.dev/docs/making-a-progressive-web-app/#customization
一些初步考虑:
"react": "^16.8.2",
"react-scripts": "2.1.5"
我创建了一个新的 React 应用程序,我需要实施推送通知。 按照 this 教程,我能够在 5 分钟内起床并 运行,但现在我必须将相同的策略(有点)实施到 React 应用程序中。
我面临的问题是我能够订阅通知 API,但我不确定如何编辑 service-worker.js
文件以添加事件侦听器以捕获push
事件(google 指南中的 Handle a Push Event
章节)
可以使用 Create React App 自定义您的 Service Worker,但可能会非常困难和棘手。
开箱即用,CRA 将 Workbox GenerateSW webpack plugin to generate service-worker.js
file, and you cannot inject any code to it (you could 与 CRA@1 一起使用,自 CRA@2 起不再使用 Workbox)
你有多种策略,我将从最简单的开始。
解决方案 1:提供您自己的 service-worker 文件
- 在
src/index.js
中启用服务工作者:// serviceWorker.unregister() serviceWorker.register()
在
src/serviceWorker.js
中注册您的自定义文件:// 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`;
当 运行 开发服务器时,您必须更改名称,CRA 为
service-worker.js
提供 mock
在
public/
文件夹中,创建custom-service-worker.js
文件。 Webpack 会将其 原样 复制到build/
文件夹
优点:快速、肮脏的胜利
缺点:你的自定义文件没有用Webpack处理(没有导入),你必须自己实现网络缓存逻辑(假设你想要一个 PWA)因为你绕过了 Workbox 插件
解决方案 2:将您的代码附加到生成的 service-worker
它有一个模块:cra-append-sw。 您负责提供附加代码。
优点:设置简单,利用 GenerateSW
缺点:附加代码使用 Babel/Webpack 处理,但不使用 CRA 的配置(您可以选择退出) .仍然使用为您处理网络缓存的 GenerateSW。不确定在本地开发时是否有效
解决方案 3:在自定义 service-worker 文件中使用 Workbox
应用解决方案 #1 的前两个步骤:更改
src/index.js
和src/serviceWorker.js
在
src/
文件夹中,创建custom-service-worker.js
文件。它将被Webpack处理,所以你可以使用ES2016/TypeScript语法并导入模块/* eslint no-restricted-globals: "off" */ import * as precaching from 'workbox-precaching' // your own imports if (self.__precacheManifest) { precaching.precacheAndRoute(self.__precacheManifest) } // your own code
-
npm add --save-dev react-app-rewired
- 在
package.json
中,在"scripts"
中,将react-scripts
替换为react-app-rewired
调整 webpack 配置:在根文件夹中创建
config-overrides.js
: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 } }
优点:你可以在 service-worker 文件中使用 ES2016/TypeScript 代码。您仍然受益于 Workbox 网络缓存设施,并对其进行完全控制
缺点:复杂且脆弱,因为有多个配置 hack。
我使用了最后一个解决方案,因为我需要来自 Workbox 的缓存代码和我的 service worker 文件中的一些 import
。
react-app-rewire-workbox 可能有助于简化 Webpack 配置(主应用程序配置)。待测试。
Create React App 4 - 2020 年 10 月发布
Starting with Create React App 4, you have full control over customizing the logic in this service worker, by creating your own src/service-worker.js file, or customizing the one added by the cra-template-pwa (or cra-template-pwa-typescript) template. You can use additional modules from the Workbox project, add in a push notification library, or remove some of the default caching logic.
https://create-react-app.dev/docs/making-a-progressive-web-app/#customization