[英]Sending message from register service worker to client page
當注冊 onupdatefound function 被觸發時,我試圖向 app.js 發送一個變量(布爾值),所以每當收到新的更新時 app.js 就會知道,然后我可以顯示一個帶有刷新按鈕的彈出窗口。
我已經實現的大部分部分,我只是對我的 app.js 將如何接收關於“消息”addEventListener 的數據感到困惑,因為我無法從中接收任何數據。
提前謝謝你。
registerServiceWorker.js
registration.onupdatefound = () => {
console.log('sw onupdatefound');
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
console.log('sw onstatechange',navigator.serviceWorker);
if (installingWorker.state === 'installed') {
console.log('sw onstatechange//',navigator.serviceWorker);
if (navigator.serviceWorker.controller) {
// available; please refresh." message in your web app.
console.log('New content is available; please refresh...');
navigator.serviceWorker.controller.postMessage({
data: {
toUpdate: true,
},
});
} else {
console.log('Content is cached for offline use.');
}
}
};
};
服務工作者.js
self.addEventListener('fetch', (event) => {
console.log('in fetch of s-w');
self.clients.matchAll().then(all => all.map(client => client.postMessage(event)));
});
self.addEventListener('message', (event) => {
console.log('event msg from s-w', event.data);
if (event.data.toUpdate) {
console.log('updating');
self.skipWaiting();
}
// Select who we want to respond to
self.clients.matchAll().then(all => all.map(client => client.postMessage(event.data)));
self.clients
.matchAll({
includeUncontrolled: true,
type: 'window',
})
.then((clients) => {
clients.postMessage(event.data);
if (clients && clients.length) {
// Send a response - the clients
// array is ordered by last focused
clients[0].postMessage(event.data);
}
});
});
應用程序.js
navigator.serviceWorker.onmessage = (event) => {
console.log('event in app.js nav on msg', event);
if (event.data.toUpdate) {
alert('Please refresh your page to upadate service worker');
}
};
window.addEventListener('message', (event) => { console.log('new event ====>', event); });
在 CRA v3 中, onSuccess
和onUpdate
回調可以作為options
傳遞給serviceWorker.register
。
通常,這些回調會更新應用程序商店,這反過來會影響 UI 組件。
對於最小的 CRA v3 演示:
// App.js
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Hello World'};
// setup callback to get
// service worker installation status
//
this.props.listen(status => {
this.setState({message: status});
});
}
render() {
return (
<p className="App">{this.state.message}</p>
);
}
}
export default App;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
const {listen, onUpdate, onSuccess} = setupStatusTransfer();
ReactDOM.render(
<React.StrictMode>
<App listen={ listen } />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.register({
onUpdate,
onSuccess
});
function setupStatusTransfer() {
let cb;
let status;
const send = s => {
status = s;
if(cb && status) cb(status);
}
const listen = callback => {
cb = callback;
send(status);
};
return {
listen,
onUpdate: makeSendStatus(send, 'update'),
onSuccess: makeSendStatus(send, 'success')
};
}
function makeSendStatus(send, status) {
return _registration => {
send(status);
};
}
另請參閱: 讓用戶知道您何時在 Create React App 中更新了 Service Worker 。
您是否正在嘗試通知服務人員正在服務的所有當前打開的選項卡(窗口)? 這可能有點問題,因為您依賴舊的服務人員來正確處理新注冊發布給它的消息 - 最終您不知道當前活動的服務人員有多過時。
話雖如此,要在 React 組件中處理postMessages
,您應該使用componentDidMount
和componentWillUnmount
生命周期方法(或useEffect
掛鈎)。
以下簡化示例使用帶有workerize-loader
的專用 Web Worker,但原理保持不變:
// App.js
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {count: '?'};
const nf = new Intl.NumberFormat(navigator.language, {minimumIntegerDigits: 6});
this.receive = event => {
const { data: count } = event;
if (typeof count === 'number') {
this.setState({count: nf.format(count)});
}
};
}
componentDidMount() {
this.props.worker.addEventListener('message', this.receive);
}
componentWillUnmount() {
this.props.worker.removeEventListener('message', this.receive);
}
render() {
return (
<p className="App">{this.state.count}</p>
);
}
}
export default App;
// App.js with hooks
import React, { useState, useEffect } from 'react';
import './App.css';
function App({ worker }) {
const [count, setCount] = useState('?');
const subscribe = () => {
return listenForUpdates(worker, setCount);
}
useEffect(subscribe, [worker]);
return (
<p className="App">{ count }</p>
);
}
function listenForUpdates(worker, setCount) {
const nf = new Intl.NumberFormat(
navigator.language,
{minimumIntegerDigits: 6}
);
const receive = ({ data }) => {
if (typeof data === 'number') {
setCount(nf.format(data));
}
};
const cleanup = () => {
worker.removeEventListener('message', receive)
};
worker.addEventListener('message', receive);
return cleanup;
}
export default App;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// eslint-disable-next-line import/no-webpack-loader-syntax
import worker from 'workerize-loader!./worker';
let instance = worker();
instance.start(1000);
ReactDOM.render(
<React.StrictMode>
<App worker={ instance } />
</React.StrictMode>,
document.getElementById('root')
);
// worker.js for https://github.com/developit/workerize-loader
//
export function start(delay) {
let count = 0;
const sendAndUpdate = () => {
postMessage(count);
++count;
};
setInterval(sendAndUpdate, delay);
}
addEventListener
和removeEventListener
應該在可從navigator.serviceWorker.controller
獲得的ServiceWorker
接口上可用。
(您的app.js
示例嘗試使用navigator.serviceWorker.onmessage
- 但navigator.serviceWorker
是ServiceWorkerContainer
- ServiceWorker
位於navigator.serviceWorker.controller
- 這是可以找到onmessage
屬性的位置)。
更新:使用 Create React App 4 ServiceWorker已成為可選,即不再包含在默認 CRA 模板中。
ServiceWorker 包含在cra-template-pwa
(制作 Progessive Web 應用程序)中,該應用程序本身通過Workbox庫使用 ServiceWorker。
npx create-react-app my-app --template cra-template-pwa
ServiceWorker 注冊邏輯已被隔離到serviceWorkerRegistration.js
- 與service-worker.js
分開。 此外,ServiceWorker 僅針對正在服務的生產構建進行注冊。 構建:
yarn run build
然后發球,例如:
http-server -c-1 ./build
最小cra-template-pwa
v4 演示:
// file: registrationStatus.js
import { useState, useEffect } from 'react';
let current = 'Unchanged';
const subscriptions = new Set();
function subscribe(setStatus) {
subscriptions.add(setStatus);
return () => {
subscriptions.delete(setStatus);
};
}
function dispatch(status) {
current = status;
subscriptions.forEach((setStatus) => setStatus(status));
}
function useStatus() {
const [status, setStatus] = useState(current);
useEffect(() => subscribe(setStatus), []);
return status;
}
// Updated but waiting for stale tabs to close
function onUpdate(_registration) {
dispatch('Updated');
}
// Updated and running
function onSuccess(_registration) {
dispatch('Successful');
}
export { useStatus, onUpdate, onSuccess };
// file: App.js
import { useStatus } from './registrationStatus';
function App() {
const status = useStatus();
return <p className="App">Registration status: {status}</p>;
}
export default App;
// file: index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';
import { onUpdate, onSuccess } from './registrationStatus';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
serviceWorkerRegistration.register({
onUpdate,
onSuccess,
});
reportWebVitals();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.