推荐答案
Shared Workers 允许多个浏览上下文(例如,不同的浏览器标签页、iframe 或窗口)共享同一个 worker 线程。它们提供了一种在多个脚本之间进行通信和共享数据的机制。与 Web Workers 不同,Web Workers 是每个脚本或页面独有的,Shared Workers 则在多个脚本之间共享。
用法:
- 创建 Shared Worker: 使用
new SharedWorker('worker.js')创建,传入 worker 脚本的 URL。 - 连接到 Shared Worker: 每个需要使用 Shared Worker 的脚本都必须通过
port属性获取一个MessagePort对象,并使用port.start()启动连接。 - 消息传递: 使用
port.postMessage()发送消息到 worker,使用port.onmessage监听来自 worker 的消息。 - Shared Worker 脚本: Worker 脚本通过
onconnect事件监听连接请求,并在事件处理程序中获取新的MessagePort对象,然后像标准 Web Worker 一样进行消息传递。
与 Web Workers 的区别:
- 作用域: Web Workers 是每个脚本或页面独有的,Shared Workers 在多个脚本之间共享。
- 生命周期: Web Worker 的生命周期与其创建的脚本相关联,而 Shared Worker 的生命周期与其连接的数量有关,当没有页面连接到它时,Shared Worker 会被销毁。
- 连接: Web Worker 直接与创建它的脚本通信,而 Shared Worker 需要通过
MessagePort对象与多个脚本进行通信。
本题详细解读
Shared Workers 的工作原理
Shared Workers 允许不同浏览上下文(例如,不同标签页、iframe 等)共享同一个 JavaScript 脚本的执行线程。这与 Web Workers 的主要区别在于,Web Workers 是每个页面或脚本独有的。
创建和连接过程:
- 创建 Shared Worker: 使用
new SharedWorker('worker.js')创建一个 Shared Worker。浏览器会加载worker.js文件,但不会立即执行其中的代码。 - 首次连接: 当第一个页面调用
new SharedWorker()时,worker 脚本的onconnect事件会被触发。这个事件回调函数会接收到一个MessagePort对象。你可以使用event.ports[0]或解构赋值const [port] = event.ports来获取这个端口。 - 建立连接: 每个需要与 Shared Worker 通信的脚本(包括创建它的脚本)都必须使用
worker.port.start()来显式启动连接。 此时,SharedWorker实例的port属性是一个MessagePort对象。 - 后续连接: 当其他页面或脚本调用
new SharedWorker()并成功建立连接(port.start())时,Worker 脚本的onconnect事件会被再次触发,这样每个连接都有一个唯一的MessagePort对象用于通信。 - 消息传递:
- 页面通过
port.postMessage(message)向 Shared Worker 发送消息。 - Shared Worker 使用
port.onmessage = function(event) { ... }接收来自页面的消息,并通过port.postMessage(response)回复。
- 页面通过
- Shared Worker 生命周期: Shared Worker 的生命周期取决于与其连接的页面或脚本的数量。当没有任何页面或脚本连接到 Shared Worker 时,它会被浏览器自动销毁。
Shared Worker 的代码结构
HTML/JS (index.html/index.js):
// www.javascriptcn.com code example
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Shared Worker Example</title>
</head>
<body>
<button id="sendBtn">Send Message</button>
<div id="output"></div>
<script src="index.js"></script>
</body>
</html>// www.javascriptcn.com code example
// index.js
const worker = new SharedWorker('worker.js');
const outputDiv = document.getElementById('output');
const sendBtn = document.getElementById('sendBtn');
worker.port.start();
worker.port.onmessage = function(event) {
outputDiv.innerHTML += `<p>Received from worker: ${event.data}</p>`;
};
sendBtn.addEventListener('click', () => {
const message = 'Hello from page';
worker.port.postMessage(message);
outputDiv.innerHTML += `<p>Sent to worker: ${message}</p>`;
});
//另一个页面,例如index2.html,也会连接到同一个worker
//index2.js的代码与index.js类似,仅输出div和发送按钮的id不同,可复用worker的逻辑Worker (worker.js):
// www.javascriptcn.com code example
// worker.js
onconnect = function(e) {
const port = e.ports[0];
port.onmessage = function(event) {
console.log('Message received by worker:', event.data);
port.postMessage('Worker received your message: ' + event.data);
};
port.postMessage('Worker is ready');
};Web Workers 与 Shared Workers 的主要区别
| 特性 | Web Workers | Shared Workers |
|---|---|---|
| 作用域 | 每个页面或脚本独有 | 多个页面或脚本共享 |
| 生命周期 | 与创建它的脚本关联 | 与连接的数量关联,无连接则销毁 |
| 通信方式 | 直接与创建它的脚本通信 | 通过 MessagePort 对象与多个脚本通信 |
| 应用场景 | 执行单个页面内的耗时任务,避免阻塞主线程 | 多个页面间共享数据、状态或复杂计算 |
| 创建方式 | new Worker('worker.js') |
new SharedWorker('worker.js') |
| 连接方式 | 直接通过 worker 实例的 postMessage 方法和 onmessage 事件 |
通过获取 MessagePort 对象并调用 start 进行连接,使用 postMessage 和 onmessage进行通信 |