最近有个需求要在浏览器页签之间实现信息共享,由第三方包处理。看过人家的效果介绍,嚯哦,闪瞎我的双眼!没见识过的技术,怎么也得看一看呀🧐

介绍

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域SharedWorkerGlobalScope 。

备注:  如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。

又称共享线程。之所以可以在多个浏览器页签中共享信息,是因为在页签的html中创建SharedWorker时,如果第一个参数相同,则视为同一线程,所以只要保证创建时的第一个参数相同,就可以在多页签间共享信息。

api

下面给了一些基本的api使用。以下api的使用都基于SharedWorker的实例对象的port属性。

创建

使用构造函数SharedWorker()创建SharedWorker实例。它有三个参数如下

MDN解释
image.png

举例

const sharedWorker = new SharedWorker('被共享的js文件url', '标识woker的名字', 可选属性对象);

自我“治愈”:

第一个参数:是被共享的js的url,要同源! 在共享线程中,我们总要做点什么。在哪里做呢?就在这个被共享的js文件中捣鼓。

第二个参数:可以标识当前的worker。在下文提到的调试中,我们会用到。主要用来确定,这个worker是不是我们现在写的worker,万一浏览器里有其他人写的worker呢?

第三个参数: 还没有深究过,这里就不介绍了。

启用

这里先随便给个url做示例~

const sharedWorker = new SharedWorker('sharedWorker.js', 'test worker');

// 开启与共享线程的连接
sharedWorker.port.start();

MDN解释
image.png

关于start()的使用,可以参考下文中填脑坑的第一条。

上面的MessagePort对象就是sharedWorker.port。所有关于浏览器页签和共享线程的通信都是通过sharedWorker.port来控制和管理。

关闭

和上面的启用基本一样,只不过名字换成了close。即sharedWorker.port.close()执行之后,就关闭了当前浏览器页签与共享线程间的连接,不再和共享线程互通消息。

消息通信

就是sharedWorker.port.postMessage(message, transferList).

第一个参数:当前浏览器页签想要发给共享线程的信息,可以是任意数据类型

第二个参数:暂不深究

调试

点击打开调试页面👉chrome://inspect/#workers 可以看到下面的内容
image.png
调试控制台大家可以理解为ChromeDevTools,一模一样。可以在上面看到自己在共享js文件中写的console

❗️❗️❗️注意

  • 单页应用的时候记得及时关闭worker,否则常驻
  • 每创建一个worker,如果参数url都一样,那么会视为使用同一个共享线程,共享线程会分配一个port对象来区分不同使用者。即SharedWorker实例对象的port可以用来区分不同的页签。

关于共享js文件
image.png

还有一点。因为有同源限制,所以需要开一个服务器才能看到信息共享效果。可以试试VS CodeLive Server插件.

案例

案例准备了两个文件,一个是共享的js文件,一个是html文件。

测试效果的时候,最少在浏览器上打开两个相同的页签。无论点击哪个页签的【获取消息】,两个页签都会alert一个3出来。

共享js文件

sharedWorker.js

let number = 3
// 储存所有port
this.ports = []
onconnect = e => {
    const port = e.ports[0];
    !this.ports.includes(port) && this.ports.push(port)
    // 监听浏览器页签发送的消息
    port.onmessage = (e) => {
        switch (e.data) {
                case 'getData':
                        // 广播:给所有port发消息
                        broadcast(number)
                        break;
                case 'close':
                        clearInvalidPort(port)
                        break;

                default:
                        break;
        }
    }
}

function broadcast(message) {
	console.log('ports', this.ports)
	this.ports.forEach(port => {
                // 给浏览器页签发消息
		port.postMessage(message)
	})
}

function clearInvalidPort(port) {
	const index = this.ports.findIndex(item => item === port);
	if (~index) {
		this.ports.splice(index, 1);
	}
}

html文件

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
</head>
<body>
 <button id="btn">获取消息</button><br/>
 <button id="closeBtn">关闭sharedWorker</button>
 <script>
   let myWorker = new SharedWorker('./sharedWorker.js', 'test worker')
   
   // 监听共享线程传递的消息
   myWorker.port.onmessage = (e) => alert(e.data);

   btn.addEventListener('click', e=> {
     // 给共享线程发送消息
     myWorker.port.postMessage('getData');
   });
   closeBtn.addEventListener('click', e=> {
     // 取消该port在共享线程中的存储[广播用的]
     myWorker.port.postMessage('close');
     // 关闭与共享线程的连接
     myWorker.port.close();
   });

   window.onbeforeunload = () => {
     myWorker.port.postMessage('close');
     myWorker.port.close();
   };
 </script>
</body>
</html>

填脑坑

自己在学习过程中不理解的点。

MessagePort对象一定要调用start()吗?

不一定。

  • 如果给MessagePort对象的onmessage属性赋值,就默认开启与共享线程的连接。
  • 如果对象使用addEventListener()监听message,就需要调用start()

共享js文件中的onconnect函数为什么用e.port[0]获取port对象?没有其他的获取方式了吗?

因为e.port是只包括一个MessagePort对象的数组。没有其他方式。

共享js文件中onconnect怎么解释?

新客户端连接的时候会触发。
具体可以看下这里

参考链接

MDN SharedWorker

MDN MessagePort

MDN postMessage

sharedWorker 实现多页面通信

JS SharedWorker详细介绍/广播/Vue使用

原文地址:http://www.cnblogs.com/zoexu/p/16790215.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性