簡體   English   中英

將消息從注冊服務人員發送到客戶端頁面

[英]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 中, onSuccessonUpdate回調可以作為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 ,您應該使用componentDidMountcomponentWillUnmount生命周期方法(或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);
}

addEventListenerremoveEventListener應該在可從navigator.serviceWorker.controller獲得的ServiceWorker接口上可用。

(您的app.js示例嘗試使用navigator.serviceWorker.onmessage - 但navigator.serviceWorkerServiceWorkerContainer - 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM