上一章介绍了Worker
的全局作用域,了解到DedicatedWorkerGlobalScope
是继承自WorkerGlobalScope
的;
这一章介绍的SharedWorkerGlobalScope
也是继承自WorkerGlobalScope
的,那么SharedWorkerGlobalScope
和DedicatedWorkerGlobalScope
有什么区别呢?
SharedWorkerGlobalScope
SharedWorkerGlobalScope
和DedicatedWorkerGlobalScope
一样都是一个全局作用域,它的实例是SharedWorker
,它的原型是WorkerGlobalScope
,它的构造函数是SharedWorkerGlobalScope
。
在SharedWorker
中可以使用self
关键字来访问SharedWorkerGlobalScope
的实例,也可以使用this
关键字来访问SharedWorkerGlobalScope
的实例。
graph LR
EventTarget --> WorkerGlobalScope
WorkerGlobalScope --> DedicatedWorkerGlobalScope
DedicatedWorkerGlobalScope --> Worker
WorkerGlobalScope --> SharedWorkerGlobalScope
SharedWorkerGlobalScope --> SharedWorker
他们之间的关系如上图所示。
SharedWorkerGlobalScope
的属性
SharedWorkerGlobalScope
的属性大多数和DedicatedWorkerGlobalScope
一样,只是少了onmessage
和onerror
,因为SharedWorkerGlobalScope
是多个DedicatedWorkerGlobalScope
共享的,所以SharedWorkerGlobalScope
的onmessage
和onerror
是多个DedicatedWorkerGlobalScope
共享的。
这里直接通过函数签名的方式来介绍SharedWorkerGlobalScope
的属性。
interface SharedWorkerGlobalScope extends WorkerGlobalScope {
readonly name: string;
close(): void;
onconnect: EventHandler
}
name
:SharedWorkerGlobalScope
的名字,它是只读的,只能在SharedWorker
的构造函数中设置,如果使用相同的name
创建多个SharedWorker
,那么它们会共享同一个SharedWorkerGlobalScope
。close()
:关闭SharedWorkerGlobalScope
,它会丢弃所有的等待执行的任务。onconnect
:当有新的DedicatedWorkerGlobalScope
连接到SharedWorkerGlobalScope
时,会触发onconnect
事件。
可以对比一下DedicatedWorkerGlobalScope
的属性,其实相差不大,只是少了onmessage
和onerror
,多了onconnect
。
数据共享
DedicatedWorkerGlobalScope
是通过postMessage
来与DedicatedWorkerGlobalScope
通信的,而SharedWorkerGlobalScope
是通过onconnect
来与DedicatedWorkerGlobalScope
通信的。
SharedWorkerGlobalScope
的onconnect
事件会返回一个MessagePort
,它是一个双向的通信通道,可以通过它来与DedicatedWorkerGlobalScope
通信。
interface MessagePort extends EventTarget {
readonly name: string;
close(): void;
postMessage(message: any, transfer?: Transferable[]): void;
start(): void;
onmessage: EventHandler;
onmessageerror: EventHandler;
}
看函数签名可以发现MessagePort
也是继承自EventTarget
的,这就意味着MessagePort
也可以使用addEventListener
来监听事件。
MessagePort
自带的属性和方法不难发现和DedicatedWorkerGlobalScope
的是相同的,只是多了一个sart
方法。
不同于DedicatedWorkerGlobalScope
的是,MessagePort
是双向的,或者说MessagePort
是双向的一边,两个MessagePort
形成一个双向的通信通道。
MessagePort
是Channel Messaging API
的一部分,这又引出了Channel Messaging API
。
Channel Messaging API
Channel Messaging API
允许我们创建一个双向的通信通道,它的实例是MessageChannel
,它的原型是EventTarget
,它的构造函数是MessageChannel
。
interface MessageChannel extends EventTarget {
readonly port1: MessagePort;
readonly port2: MessagePort;
}
可以看到MessageChannel
有两个只读属性,分别是port1
和port2
,它们都是MessagePort
的实例,它们之间形成一个双向的通信通道。
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
console.log(event.data);
};
channel.port2.postMessage('hello');
上面的代码中,channel.port1
和channel.port2
是一个双向的通信通道,它们之间可以互相通信。
当然通常我们不会这样使用MessageChannel
,那可以用来做什么呢,我们可以实现数据拷贝:
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
console.log(event.data);
};
var obj = {
a: 1 };
obj.b = obj;
channel.port2.postMessage(obj);
这里使用的是结构化克隆算法,它可以克隆复杂的数据结构,而不是简单的拷贝引用。
SharedWorker
当我们了解了SharedWorkerGlobalScope
、MessagePort
、MessageChannel
之后,我们对SharedWorker
的使用就不会陌生了。
// main.js
const worker = new SharedWorker('worker.js', 'worker');
worker.port.onmessage = (event) => {
console.log(event.data);
};
// 在讲 SharedWorker 的文章中讲过
// 如果使用 onmessage 监听,会自动调用 start 方法
// 如果使用 addEventListener 监听,需要手动调用 start 方法
// 所以下面的代码可以省略
worker.port.start();
worker.port.postMessage('hello');
当我们有上面的基础之后,来看一下上面构建的SharedWorker
的代码,不难发现都是我们之前讲过的:
new SharedWorker
:是SharedWorker
的构造函数,这个是属于SharedWorker
的worker.port
:是MessagePort
的实例,也是属于SharedWorker
的worker.port.onmessage
:是MessagePort
的onmessage
事件worker.port.start()
:是MessagePort
的start
方法worker.port.postMessage
:是MessagePort
的postMessage
方法
通过上面的代码就可以推断出SharedWorker
的函数签名:
interface SharedWorker extends EventTarget {
constructor(scriptURL: string, name?: string);
readonly port: MessagePort;
}
事实的函数签名也是和上面的一致。
上面是在主函数中创建SharedWorker
,那么在SharedWorker
中是怎么使用的呢,我们来看一下:
// worker.js
onconnect = (event) => {
const port = event.ports[0];
port.onmessage = (event) => {
console.log(event.data);
};
port.start();
port.postMessage('hello');
};
在SharedWorker
中,只需要监听onconnect
事件,然后在事件中获取MessagePort
,然后就可以使用MessagePort
的方法了。
这个是属于SharedWorkerGlobalScope
的,可以把文章网上翻翻,就可以看到了。
总结
通过这次分析SharedWorkerGlobalScope
之后,我们对SharedWorker
的使用就不会陌生了;
它的使用和Worker
是一样的,不同是他们的全局作用域不同,只是会有一些特定的方法,都是WorkerGlobalScope
的子类,所以它可以使用WorkerGlobalScope
的所有方法。
也通过这次分析,我们认识到MessagePort
、MessageChannel
,这些都是SharedWorker
的基础也是核心,它们之间的关系也是非常重要的。
而且认识到MessagePort
、MessageChannel
之后,其实跨页面通信也可以使用它们,也可以使用它们来做一些其他的事情,这些就看自己的想象力了。
历史章节和预告
- 🎉🎉🎉 Web Workers 使用秘籍,祝您早日通关前端多线程!
- 🥳🥳🥳Worker中还可以创建多个Worker,打开多线程编程的大门
- ✨✨✨ ServiceWorker 让你的网页拥抱服务端的能力
- 🎊🎊🎊深入 ServiceWorker,消息推送,后台同步,一网打尽!
- 🚂🚂🚂 ServiceWorker -> PWA的基石,在线离线都能玩!
- 💞💞💞SharedWorker 让你多个页面相互通信
- 🎁🎁🎁详解 Web Worker,不再止步于会用!
- 当前章节
- ServiceWorkerGlobalScope 让你重新认识 ServiceWorker
- 构思中...