各位听众,欢迎来到今天的“线程共享,快乐编程”讲座!
咳咳,今天咱们聊点刺激的——SharedWorker
。 各位都知道,Web Worker 就像一个勤劳的小弟,帮你分担主线程的计算压力,让页面不再卡顿。但是,如果你想让多个标签页共享同一个小弟,一起干活,那普通的 Web Worker 就歇菜了。 这时候,SharedWorker
就该闪亮登场了!
一、SharedWorker
:共享的苦力,快乐的通信
简单来说,SharedWorker
就是一个可以被多个浏览上下文(例如,多个标签页、iframe)共享的 Web Worker。 就像一个公共的线程池,大家可以往里面扔任务,然后共享结果。
那么,它和普通 Web Worker 有什么不同呢?
特性 | Web Worker | SharedWorker |
---|---|---|
共享范围 | 只能被创建它的页面使用 | 可以被同源的多个页面共享 |
通信方式 | 直接通过 postMessage 通信 |
需要通过 port 对象进行通信,建立连接后才能通信 |
生命周期 | 页面关闭后,Worker 也会关闭 | 只要有任何一个页面连接着,Worker 就不会关闭 |
创建方式 | new Worker('worker.js') |
new SharedWorker('shared_worker.js') |
应用场景 | 独立计算任务,不需要跨页面共享数据 | 多个页面需要共享数据、状态,例如多人协作编辑、实时数据同步 |
总结:
- Web Worker:独享的劳动力,干完就走,互不打扰。
- SharedWorker:共享的劳动力,大家一起用,共享数据。
二、SharedWorker
的基本使用
1. 创建 SharedWorker
文件 (shared_worker.js
)
这部分是 SharedWorker
的核心逻辑,相当于“小弟”的大脑。
// shared_worker.js
let connections = 0;
self.addEventListener('connect', (event) => {
const port = event.ports[0];
connections++;
console.log(`新连接!当前连接数:${connections}`);
port.addEventListener('message', (e) => {
const data = e.data;
console.log(`收到消息:${data}`);
// 处理消息,并将结果返回给所有连接的客户端
const result = `[SharedWorker] 收到:${data},处理完毕!`;
port.postMessage(result);
// 广播消息给所有连接的客户端
for (let p of self.clients) {
p.postMessage(result);
}
});
port.start(); // 启动端口,开始接收消息
port.postMessage(`[SharedWorker] 欢迎连接!`);
});
self.addEventListener('close', (event) => {
connections--;
console.log(`连接关闭!当前连接数:${connections}`);
});
代码解释:
self.addEventListener('connect', ...)
:监听客户端的连接请求。event.ports[0]
:获取与客户端通信的MessagePort
对象。port.addEventListener('message', ...)
:监听客户端发送的消息。port.postMessage(...)
:向客户端发送消息。port.start()
:启动端口,开始接收消息 (必须调用)。self.addEventListener('close', ...)
: 监听连接关闭。
2. 在 HTML 页面中使用 SharedWorker
<!DOCTYPE html>
<html>
<head>
<title>SharedWorker Example</title>
</head>
<body>
<h1>SharedWorker Example</h1>
<button id="sendMessageBtn">发送消息</button>
<div id="messageArea"></div>
<script>
const messageArea = document.getElementById('messageArea');
const sendMessageBtn = document.getElementById('sendMessageBtn');
const sharedWorker = new SharedWorker('shared_worker.js');
// 获取 port 对象
const port = sharedWorker.port;
// 监听 SharedWorker 发送的消息
port.addEventListener('message', (event) => {
const message = event.data;
messageArea.innerHTML += `<p>收到消息:${message}</p>`;
});
// 启动端口
port.start();
// 发送消息给 SharedWorker
sendMessageBtn.addEventListener('click', () => {
const message = `来自标签页的消息:${Date.now()}`;
port.postMessage(message);
messageArea.innerHTML += `<p>发送消息:${message}</p>`;
});
// 处理错误
sharedWorker.onerror = (error) => {
console.error('SharedWorker 错误:', error);
};
// 可选:在页面unload时关闭连接
window.addEventListener('unload', () => {
// sharedWorker.port.close(); //可选,关闭连接
// sharedWorker.close(); // 这个是关闭 worker的,会停止worker的运行
});
</script>
</body>
</html>
代码解释:
new SharedWorker('shared_worker.js')
:创建SharedWorker
实例。sharedWorker.port
:获取用于通信的MessagePort
对象。port.addEventListener('message', ...)
:监听SharedWorker
发送的消息。port.start()
:启动端口,开始接收消息 (必须调用)。port.postMessage(...)
:向SharedWorker
发送消息。sharedWorker.onerror
:监听错误
重要提示:
- 必须调用
port.start()
才能开始接收消息。 SharedWorker
的 URL 必须与创建它的页面同源。- 一个
SharedWorker
实例可以被多个页面共享,所以要小心处理并发问题。
3. 多页面测试
将上面的 HTML 代码复制到两个或多个 HTML 文件中,然后在浏览器中打开这些文件。 你会发现,无论哪个页面发送消息,所有页面都会收到消息,说明 SharedWorker
正在多个标签页之间共享数据。
三、SharedWorker
的高级用法
1. 广播消息
SharedWorker
可以向所有连接的客户端广播消息。 这在多人协作编辑、实时数据同步等场景中非常有用。
在 shared_worker.js
中,可以使用 clients
对象来获取所有连接的客户端,并向它们发送消息。
// shared_worker.js
self.addEventListener('connect', (event) => {
const port = event.ports[0];
port.addEventListener('message', (e) => {
const data = e.data;
console.log(`收到消息:${data}`);
// 广播消息给所有连接的客户端
const message = `[SharedWorker] 广播:${data}`;
self.clients.matchAll().then(clients => { // 改成matchAll
clients.forEach(client => {
client.postMessage(message);
});
});
port.postMessage(`[SharedWorker] 收到:${data},处理完毕!`);
});
port.start();
});
代码解释:
self.clients.matchAll()
:获取所有连接的客户端。clients.forEach(client => ...)
:遍历所有客户端,并向它们发送消息。
2. 共享数据
SharedWorker
可以使用 SharedArrayBuffer
和 Atomics
API 来在多个页面之间共享数据。 但是,SharedArrayBuffer
需要开启跨域隔离,配置比较复杂,这里不做详细介绍。
3. 复杂的通信模式
除了简单的消息传递,SharedWorker
还可以实现更复杂的通信模式,例如:
- 请求-响应模式:客户端发送请求,
SharedWorker
处理请求并返回响应。 - 发布-订阅模式:客户端订阅某些主题,
SharedWorker
在主题发生变化时向订阅者发送通知。
这些复杂的通信模式可以通过自定义协议来实现。
四、SharedWorker
的应用场景
- 多人协作编辑:多个用户可以同时编辑同一个文档,
SharedWorker
负责同步数据。 - 实时数据同步:多个页面需要显示相同的数据,
SharedWorker
负责从服务器获取数据并同步到所有页面。 - 游戏:多个玩家可以通过
SharedWorker
共享游戏状态。 - 缓存:
SharedWorker
可以作为全局缓存,缓存一些常用的数据,避免重复请求。 - 后台任务:
SharedWorker
可以在后台执行一些任务,例如定时任务、数据同步等。
五、SharedWorker
的注意事项
- 同源策略:
SharedWorker
的 URL 必须与创建它的页面同源。 - 并发问题:多个页面可以同时访问
SharedWorker
,所以要小心处理并发问题。可以使用锁、信号量等机制来保证数据的一致性。 - 内存泄漏:如果
SharedWorker
中存在循环引用,可能会导致内存泄漏。 - 调试:调试
SharedWorker
比较麻烦,可以使用浏览器的开发者工具来调试。 - 性能:
SharedWorker
虽然可以提高性能,但是也会增加 CPU 和内存的消耗。要根据实际情况合理使用。 - 兼容性:
SharedWorker
的兼容性不是很好,需要考虑兼容性问题。 - 生命周期管理:需要注意
SharedWorker
的生命周期管理,避免出现意外情况。
六、SharedWorker
的优缺点
优点:
- 可以被多个页面共享,节省资源。
- 可以实现跨页面通信,方便数据共享。
- 可以在后台执行任务,不影响页面性能。
缺点:
- 并发问题比较复杂,需要小心处理。
- 调试比较麻烦。
- 兼容性不是很好。
七、总结
SharedWorker
是一个强大的工具,可以帮助我们构建更复杂、更强大的 Web 应用。 但是,它也需要我们仔细设计和实现,才能发挥其最大的威力。
希望今天的讲座能帮助大家更好地理解 SharedWorker
,并在实际项目中灵活运用。
最后,记住一句真理:
线程共享一时爽,并发处理火葬场!
所以,使用 SharedWorker
时,一定要小心并发问题,做好同步和互斥,才能真正享受到线程共享带来的便利。
感谢各位的收听! 我们下次再见!