从注册服务人员向客户端页面发送消息

时间:2020-07-01 17:21:19

标签: javascript reactjs web service-worker

我试图在触发注册onupdatefound函数时将变量(布尔值)发送到app.js,所以每当收到新更新时,app.js就会知道,然后我可以显示带有刷新按钮的弹出窗口

我已经实现了大部分,因为我的app.js将如何从“ message” addEventListener接收数据而感到困惑,因为我无法从其中接收任何数据。

先谢谢您

registerServiceWorker.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);
  }
 });
});

service-worker.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); });

app.js

{{1}}

1 个答案:

答案 0 :(得分:1)

在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);
  };
}

另请参阅:Let Users Know When You Have Updated Your Service Workers in Create React App


您是否要通知服务人员正在服务的所有当前打开的选项卡(窗口)?由于您依靠旧服务人员来正确处理新注册发布到其上的消息,所以这可能会有些问题-最终您不知道已过期当前活跃的服务工作者。

也就是说,要在React组件中处理postMessages,您应该使用componentDidMountcomponentWillUnmount生命周期方法(或useEffect挂钩)。

下面的简化示例将专用的Web Worker与workerize-loader一起使用,但是原理保持不变:

// 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应该可以在ServiceWorker上获得的navigator.serviceWorker.controller界面上使用。

(您的app.js示例尝试navigator.serviceWorker.onmessage-但navigator.serviceWorkerServiceWorkerContainer-在navigator.serviceWorker.controller上找到ServiceWorker-可以找到onmessage属性。

相关问题