@3m1/service-worker-updater
管理 Create React App 的 Service Worker 更新
如果您选择加入 register index.js
中serviceWorkerRegistration
回调-progressive-web-app/">Create React APP 的 PWA 版本,您可能希望允许您的用户在检测到新的 service worker 后更新应用程序。
How it works
通常,浏览器每隔几天检查一次 PWA 的新服务工作者版本,或者每当用户重新加载页面时检查一次。 但是重新加载页面并不一定会更新 service worker。 由于管理 service worker 的代码通常在 React 组件树之外,检测到新 service worker 的消息 需要通过 props 或上下文之外的另一种机制传递。 在这里,我们使用通过 document
触发的事件,该事件之前已添加了一个侦听器。 添加侦听器的组件位于 React 的组件树内,并接收并保存resgistration
对象以供以后在onLoadNewServiceWorkerAccept
回调中使用。
Install
NPM
npm install --save @3m1/service-worker-updater
Yarn
yarn add @3m1/service-worker-updater
Usage
该库由两部分组成:
onServiceWorkerUpdate
要添加到 index.js
上的 serviceWorkerRegistration.register
调用的回调。 此步骤是强制性的,否则消息将不会到达您的内部组件。
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
import { onServiceWorkerUpdate } from '@3m1/service-worker-updater'
// Render the App
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register({
onUpdate: onServiceWorkerUpdate
})
// ...
如果您已经在使用 onUpdate
回调,则需要在其中添加此回调:
serviceWorkerRegistration.register({
onUpdate: (registration) => {
// Your code goes here
// ...
// Then, call this callback:
onServiceWorkerUpdate(registration)
}
})
withServiceWorkerUpdater
HOC 包装一个将接收 2 个额外 props
的组件:
newServiceWorkerDetected
: boolean indicating if a new version of the service worker has been detected. If true
, you should offer the user some way to update the app.
onLoadNewServiceWorkerAccept
: a callback which needs to be called once the user accepts to update to the new Service Worker. You choose what actions needs to be taken by the user to update the service worker (a button, a link, a countdown, …). During its execution, the page will be reloaded in order to use the newly activated service worker. WARNING! Make sure all unsaved changes are saved before executing it.
import React from 'react'
import {
withServiceWorkerUpdater,
ServiceWorkerUpdaterProps
} from '@3m1/service-worker-updater'
const Updater = (props: ServiceWorkerUpdaterProps) => {
const { newServiceWorkerDetected, onLoadNewServiceWorkerAccept } = props
return newServiceWorkerDetected ? (
<>
New version detected.
<button onClick={onLoadNewServiceWorkerAccept}>Update!</button>
</>
) : null // If no update is available, render nothing
}
export default withServiceWorkerUpdater(Updater)
对于非 Typescript 项目,请使用以下片段:
import React from 'react'
import { withServiceWorkerUpdater } from '@3m1/service-worker-updater'
const Updater = (props) => {
const { newServiceWorkerDetected, onLoadNewServiceWorkerAccept } = props
return newServiceWorkerDetected ? (
<>
New version detected.
<button onClick={onLoadNewServiceWorkerAccept}>Update!</button>
</>
) : null // If no update is available, render nothing
}
export default withServiceWorkerUpdater(Updater)
发送给服务工作人员的消息是 {type: 'SKIP_WAITING'}
,这是 Create React APP 的 PWA 版本 期望启动其 self.skipWaiting()
方法。 如果您有不同的 service worker 配置,您可以使用第二个可选参数在此处更改它:
export default withServiceWorkerUpdater(Updater, {
message: { myCustomType: 'SKIP_WAITING' }
})
在重新加载页面之前,'Controller loaded'
将使用 console.log
记录>。 如果您想更改它,请这样做:
export default withServiceWorkerUpdater(Updater, {
log: () => console.warn('App updated!')
})
Persistence
当检测到新的服务工作者时,将触发一个事件。 如果刷新应用程序,则不会再次触发该事件,因此您将无法再通知用户服务工作者更新。 这个包以 PersistenceService
的形式提供了解决方案。
持久性服务被注入到组件中并处理刷新后的状态持久化。 该软件包带有一个默认的基于本地存储的持久化服务。 它可以这样使用:
import { LocalStoragePersistenceService } from '@3m1/service-worker-updater'
const Updater = () => {
/* Your updater component code */
}
export default withServiceWorkerUpdater(Updater, {
persistenceService: new LocalStoragePersistenceService('myApp')
})
您可以通过遵守 PersistenceService
接口定义您自己的基于其他机制的持久层:
import { PersistenceService } from '@3m1/service-worker-updater'
class YourPersistenceService implements PersistenceService {
setUpdateIsNeeded(): void {}
clear(): void {}
isUpdateNeeded(): boolean {
return false
}
}
:trophy: Thanks to
See also
- React Service Worker: A headless React component that wraps around the Navigator Service Worker API to manage your service workers. Inspired by Create React App's service worker registration script.
- Service Worker Updater - React Hook & HOC: This package provides React hook and HOC to check for service worker updates.
- @loopmode/cra-workbox-refresh: Helper for
create-react-app
v2 apps that use the workbox service worker. Displays a UI that informs the user about updates and recommends a page refresh.
License
GPL-3.0-or-later © github.com/emibcn
@3m1/service-worker-updater
Manage Create React App's Service Worker update
If you have opted-in for the register
callback of serviceWorkerRegistration
in the index.js
of the PWA version of Create React APP, you probably want to allow your users to update the application once a new service worker has been detected.
How it works
Usually, browsers check for a new service worker version of a PWA every few days, or whenever the user reloads the page. But reloading the page does not necessarily updates the service worker. As the code managing the service worker is usually outside the React components tree, the message of a new service worker detected needs to be passed through another mechanism than props or contexts. Here, we use an event triggered over document
, which will previously have been added a listener. The component that adds the listener is inside the React's components tree, and receives and saves the resgistration
object for later use in the onLoadNewServiceWorkerAccept
callback.
Install
NPM
npm install --save @3m1/service-worker-updater
Yarn
yarn add @3m1/service-worker-updater
Usage
This library is composed by 2 parts:
onServiceWorkerUpdate
Callback to be added to the serviceWorkerRegistration.register
call on your index.js
. This step is mandatory, or the message will not arrive to your inner component.
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
import { onServiceWorkerUpdate } from '@3m1/service-worker-updater'
// Render the App
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register({
onUpdate: onServiceWorkerUpdate
})
// ...
If you are already using the onUpdate
callback, you need to add this callback in there:
serviceWorkerRegistration.register({
onUpdate: (registration) => {
// Your code goes here
// ...
// Then, call this callback:
onServiceWorkerUpdate(registration)
}
})
withServiceWorkerUpdater
HOC to wrap a component which will receive 2 extra props
:
newServiceWorkerDetected
: boolean indicating if a new version of the service worker has been detected. If true
, you should offer the user some way to update the app.
onLoadNewServiceWorkerAccept
: a callback which needs to be called once the user accepts to update to the new Service Worker. You choose what actions needs to be taken by the user to update the service worker (a button, a link, a countdown, …). During its execution, the page will be reloaded in order to use the newly activated service worker. WARNING! Make sure all unsaved changes are saved before executing it.
import React from 'react'
import {
withServiceWorkerUpdater,
ServiceWorkerUpdaterProps
} from '@3m1/service-worker-updater'
const Updater = (props: ServiceWorkerUpdaterProps) => {
const { newServiceWorkerDetected, onLoadNewServiceWorkerAccept } = props
return newServiceWorkerDetected ? (
<>
New version detected.
<button onClick={onLoadNewServiceWorkerAccept}>Update!</button>
</>
) : null // If no update is available, render nothing
}
export default withServiceWorkerUpdater(Updater)
For non Typescript projects, use the following snippet:
import React from 'react'
import { withServiceWorkerUpdater } from '@3m1/service-worker-updater'
const Updater = (props) => {
const { newServiceWorkerDetected, onLoadNewServiceWorkerAccept } = props
return newServiceWorkerDetected ? (
<>
New version detected.
<button onClick={onLoadNewServiceWorkerAccept}>Update!</button>
</>
) : null // If no update is available, render nothing
}
export default withServiceWorkerUpdater(Updater)
The message sent to the service worker is {type: 'SKIP_WAITING'}
, which is the one the PWA version of Create React APP expects in order to launch its self.skipWaiting()
method. If you have a different service worker configuration, you can change it here using the second optional argument:
export default withServiceWorkerUpdater(Updater, {
message: { myCustomType: 'SKIP_WAITING' }
})
Just before reloading the page, 'Controller loaded'
will be logged with console.log
. If you want to change it, do it so:
export default withServiceWorkerUpdater(Updater, {
log: () => console.warn('App updated!')
})
Persistence
When a new service worker is detected an event is fired. If the app is refreshed, the event is not fired again so you'll no longer be able to notify users about service worker updates. This package provides a solution to that in the form of a PersistenceService
.
The persistence service is injected into the component and handles persisting the state after refresh. The package comes with a default persistence service based on local storage. It can be used thus:
import { LocalStoragePersistenceService } from '@3m1/service-worker-updater'
const Updater = () => {
/* Your updater component code */
}
export default withServiceWorkerUpdater(Updater, {
persistenceService: new LocalStoragePersistenceService('myApp')
})
You can define your own persistence layer based on other mechanisms by adhering to the PersistenceService
interface:
import { PersistenceService } from '@3m1/service-worker-updater'
class YourPersistenceService implements PersistenceService {
setUpdateIsNeeded(): void {}
clear(): void {}
isUpdateNeeded(): boolean {
return false
}
}
:trophy: Thanks to
See also
- React Service Worker: A headless React component that wraps around the Navigator Service Worker API to manage your service workers. Inspired by Create React App's service worker registration script.
- Service Worker Updater - React Hook & HOC: This package provides React hook and HOC to check for service worker updates.
- @loopmode/cra-workbox-refresh: Helper for
create-react-app
v2 apps that use the workbox service worker. Displays a UI that informs the user about updates and recommends a page refresh.
License
GPL-3.0-or-later © github.com/emibcn