SharedWorker 多页面相互通信示例详解
正文
SharedWorker
是一个新的Web Worker API
,它允许你在多个页面之间共享一个Worker
。
SharedWorker
代表一种特定类型的Worker
,可以在多个浏览器上下文中运行,比如多个页面或者多个iframe
。
什么是 SharedWorker
根据前几篇的了解,大家应该对Worker
有了一定的了解,SharedWorker
是Worker
的一种,它允许你在多个页面之间共享一个Worker
。
SharedWorker
对比于Service Worker
要简单很多,同样也是继承与Worker
,但是它的生命周期是独立于页面的。
SharedWorker
和其他Worker
的相同,都是需要同源的,它是通过url
来区分是否是同一个SharedWorker
。
使用 SharedWorker
SharedWorker
的使用很简单,只需要在创建Worker
的时候,需要传入一个name
参数,这个name
参数就是SharedWorker
的标识,相同的name
会共享同一个SharedWorker
。
const sharedWorker = new SharedWorker('worker.js', 'mySharedWorker');
默认情况下name
并不是必须的,这个时候相同的url
会共享同一个SharedWorker
。
const sharedWorker = new SharedWorker('worker.js');
创建SharedWorker
之后,我们就可以通过port
属性来和SharedWorker
进行通信了。
sharedWorker.port.onmessage = (e) => { console.log(e.data); };
在主线程中,可以通过创建出来的worker
对象中的port
属性来和SharedWorker
进行通信。
使用worker.port.onmessage
来监听SharedWorker
的消息。
在SharedWorker
中,可以通过self.onconnect
来监听SharedWorker
的连接。
在监听事件中会获取到与SharedWorker
连接的port
,通过这个port
就可以和主线程进行通信了。
self.onconnect = (e) => { const port = e.ports[0]; port.postMessage('hello'); };
通信还是通过postMessage
来进行的。
通信
上面的通信只是一个简单的示例,在主线程中创建的SharedWorker
,并使用port
来和SharedWorker
进行通信。
通常情况下,我们会使用onmessage
来监听SharedWorker
的消息,使用这种方式默认是打开状态,如果使用addEventListener
来监听SharedWorker
的消息,需要手动调用start
方法来开启。
sharedWorker.port.addEventListener('message', (e) => { console.log(e.data); }); sharedWorker.port.start();
在主线程中,我们可以通过port
来实现和SharedWorker
的通信。
// 发送消息 sharedWorker.port.postMessage('hello'); // 监听消息 sharedWorker.port.onmessage = (e) => { console.log(e.data); }; // 或者 sharedWorker.port.addEventListener('message', (e) => { console.log(e.data); }); sharedWorker.port.start();
在SharedWorker
中使用方式也是一样的,不同的是,我们需要通过self.onconnect
来监听SharedWorker
的连接。
self.onconnect = (e) => { const port = e.ports[0]; // 发送消息 port.postMessage('hello'); // 监听消息 port.onmessage = (e) => { console.log(e.data); }; // 或者 port.addEventListener('message', (e) => { console.log(e.data); }); port.start(); };
可以看到上面监听的时候,获取port
是一个数组,这是因为SharedWorker
可以和多个页面进行通信。
每个页面都会有一个port
,通过这个port
和SharedWorker
进行通信。
通常情况下我们会记录下所有的port
,然后在需要的时候进行通信。
const ports = []; self.onconnect = (e) => { const port = e.ports[0]; ports.push(port); port.onmessage = (e) => { console.log(e.data); }; }; // 发送消息 function sendMessage(message) { ports.forEach((port) => { port.postMessage(message); }); }
上面我们的案例中,我们只创建了一个SharedWorker
,所以只有一个port
,但是如果我们创建了多个SharedWorker
,那么就会有多个port
。
这里收到消息之后浏览器控制台没有任何打印信息,因为SharedWorker
的作用域中没有window
对象,所以console
、alert
等方法都是无法使用的。
如果我们需要调试SharedWorker
,可以在浏览器地址栏中输入chrome://inspect/#workers
,这样就可以看到当前页面中的SharedWorker
。
指定页面通信
上面我们拿到了所有的port
,但是我们并不知道这个port
是哪个页面的,所以我们需要在创建SharedWorker
的时候,指定每个页面的标识。
但是很遗憾,SharedWorker
并没有提供这样的功能,所以我们需要自己实现。
这里我们可以使用双数组来实现,一个数组用来存储页面的标识,一个数组用来存储port
。
const pages = []; const ports = []; self.onconnect = (e) => { const port = e.ports[0]; ports.push(port); // 获取页面标识 const page = Math.random().toString(36).substr(2); port.postMessage(page); pages.push(page); // 监听消息 port.onmessage = (e) => { const index = ports.indexOf(port); const page = pages[index]; // 这样就可以知道是哪个页面发来的消息了 console.log(page); }; };
有了上面的代码,我们就可以知道是哪个页面发来的消息了,并且也可以指定页面发送消息。
实战
上面我们已经知道了如何使用SharedWorker
,那么我们就可以使用它来实现一个简单的聊天室。
这里我们使用SharedWorker
来实现消息的转发,页面之间的通信使用postMessage
。
- SharedWorker
const uuids = []; const ports = []; self.onconnect = (e) => { const port = e.ports[0]; ports.push(port); // 获取页面标识 const uuid = Math.random().toString(36).substr(2); uuids.push(uuid); port.postMessage({ type: 'connect', uuid: uuid }); // 监听消息 port.onmessage = (e) => { const index = ports.indexOf(port); const uuid = uuids[index]; // 群发消息 broadcast({ type: 'message', sender: uuid, message: e.data.message }); }; }; // 群发消息 const broadcast = (data) => { ports.forEach((port) => { port.postMessage(data); }); }
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .chat { width: 100%; height: 100%; display: flex; flex-direction: column; } #messages { height: 500px; width: 300px; overflow: auto; border: #3498db 1px solid; padding: 0 10px; } .chat__messages { flex: 1; overflow-y: auto; margin: 4px 0; } .sender { font-size: 12px; margin: 0; padding: 0; } .sender:after { content: ' :'; } .message { width: auto; max-width: 300px; background: #3498db; padding: 10px; border-radius: 10px; margin: 10px 0; font-size: 16px; color: #fff; } </style> </head> <body> <div class="chat"> <div id="messages"> </div> <div class="chat__input"> <input type="text" id="message"> <button id="send">发送</button> </div> </div> <script> let uuid = '' const sharedWorker = new SharedWorker('sharedWorker.js'); sharedWorker.port.onmessage = (e) => { if (e.data.type === 'connect') { uuid = e.data.uuid; alert('连接成功') return; } const messageDom = ` <div class="chat__messages"> <p class="sender" ${uuid === e.data.sender ? 'style="color: #165DFF"' : ""}>${e.data.sender}</p> <p class="message">${e.data.message}</p> </div> ` document.getElementById('messages').insertAdjacentHTML('beforeend', messageDom); // 滚动到底部 document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight; }; document.getElementById('send').addEventListener('click', () => { const message = document.getElementById('message').value; if (!message) { alert('请输入内容') return; } const data = { type: 'message', message: message, } sharedWorker.port.postMessage(data); document.getElementById('message').value = ''; }); </script> </body> </html>
上面的代码我们实现了一个简单的聊天室,我们可以在多个页面打开聊天室,然后在其中一个页面发送消息,其他页面都会收到消息。
上面的代码中还有一个uuid
,=还可以基于这么来实现私聊,比如我们可以在页面中添加一个select
,然后选择一个用户,然后在发送消息的时候,将uuid
传递过去,然后在SharedWorker
中判断,如果是私聊,就只发送给指定的用户。
这一块就留给感兴趣的同学自己实现了。
SharedWorker 的关闭
上面我们实现了一个简单的聊天室,但是我们发现,当我们关闭页面的时候,SharedWorker
并没有关闭,这是因为SharedWorker
是一个长连接,当我们关闭页面的时候,SharedWorker
并没有关闭,所以我们需要在页面关闭的时候,手动关闭SharedWorker
。
// 关闭页面的时候,关闭 SharedWorker window.addEventListener('beforeunload', () => { sharedWorker.port.postMessage({ type: 'close', uuid: uuid }); }); // SharedWorker.js /** 省略其他代码 **/ onconnect = (e) => { const port = e.ports[0]; port.onmessage = (e) => { if (e.data.type === 'close') { const index = ports.indexOf(port); ports.splice(index, 1); return; } }; };
上面的代码中,我们在页面关闭的时候,发送一个close
的消息,然后在SharedWorker
中,将port
从ports
中移除,这样就可以关闭SharedWorker
了。
SharedWorker 的生命周期
SharedWorker
的生命周期没有那么复杂,不像ServiceWorker
有那么多的过程,它只有一条规则,就是当所有创建SharedWorker
的页面关闭之后,那么SharedWorker
的生命就走到了尽头,否则它就会一直常驻。
SharedWorker 还可以做什么?
聊天室只是一个小玩具,我们还可以做很多事情,我们最常见的就是跨页面登录,例如GitHub
,我们在一个页面登录,然后在其他页面就可以获取到登录信息,这就是SharedWorker
的一个应用场景。
还有一个应用场景就是,我们可以在一个页面中,监听一个WebSocket
,然后在其他页面中,发送消息,这样就可以实现跨页面的WebSocket
。
其实任何跨页面的通信,都可以使用SharedWorker
来实现,但是SharedWorker
的缺点也很明显,就是不支持IE
,所以如果你的项目需要兼容IE
,那么就不要使用SharedWorker
了。
优点很明确,就是可以跨页面通信,缺点也很明显,就是浏览器兼容确实有点捉急。
总结
本文主要介绍了SharedWorker
的基本使用,并使用SharedWorker
实现了一个简单的聊天室。
SharedWorker
的使用还是比较简单的,不同于Worker
那么单纯,SharedWorker
是一个长连接,所以我们可以在多个页面中,共享一个SharedWorker
,这样就可以实现跨页面通信了。
不同于ServiceWorker
有那么多的限制,SharedWorker
除了兼容性其实功能非常强大,且在实际项目中的应用场景也更加广泛。
以上就是SharedWorker 多页面相互通信示例详解的详细内容,更多关于SharedWorker 多个页面通信的资料请关注脚本之家其它相关文章!
相关文章
Layui Table js 模拟选中checkbox的例子
今天小编就为大家分享一篇Layui Table js 模拟选中checkbox的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2019-09-09JS向上取整、向下取整、四舍五入、取绝对值、取较大较小值及随机数代码示例
在Js中对数值进行操作的场景有,向上取整、向下取整、四舍五入、固定精度、固定长度、取整,这篇文章主要给大家介绍了关于JS向上取整、向下取整、四舍五入、取绝对值、取较大较小值及随机数的相关资料,需要的朋友可以参考下2024-04-04
最新评论