WebWorker
WebWorker(HTML5新引入技术)
一、基本概念
JS 在浏览器中默认运行在单线程环境中,所有任务(渲染、事件处理等)都在同一线程上执行。如果某个任务耗时过长,就可能导致页面冻结、响应迟缓。Web Worker允许我们创建新的线程,专门用来处理耗时任务,从而避免主进程被阻塞。
二、工作原理
Web Worker 与主线程之间采用消息传递进行通信。主线程与 Worker 线程之间不能共享同一上下文(例如不能直接访问 Dom)。只能通过postMessage
方法传递消息,Worker 接收到消息后,通过onMessage
事件处理消息,返回结果。
?什么叫做不能共享同一上下文?
上下文是指在特定环境中执行的代码和能够访问的资源。在浏览器中,主线程的上下文包括对DOM的访问、事件处理、UI渲染等。Worker 线程的上下文是独立的,没有访问DOM的能力,也无法直接和主线程共享变量、对象等。Worker 线程主要用于 计算密集型任务,例如数据处理、文件读取等,执行这些任务时并不需要与 DOM 交互,因此将它们隔离开有助于性能优化。
三、多种类型
Dedicated Worker(专用工作者)
最常见的Worker类型,专门为主线程(调用它的脚本)服务,每个Worker只能由一个主线程使用。主线程和Worker通过postMessage()和onmessage进行通信。
- 1、创建专用Worker脚本
1 |
|
- 2、在主线程中创建Worker实例
1 |
|
- 3、终止 Worker
1 |
|
Shared Worker(共享工作者)
允许多个脚本(甚至来自不同窗口、iframe 或 web worker)共享一个 Worker 实例,通过共享的通信端口(MessagePort)进行交互。允许多个 JavaScript 线程(例如多个网页窗口)与一个 Worker 进行通信,适合多个页面或多个上下文间共享资源和数据的情况。共享 Worker 的生命周期由浏览器管理,当所有连接的页面关闭时,Worker 才会结束运行。
- 创建共享Worker脚本
1 |
|
- 使用共享 Worker
1 |
|
Service Worker(服务工作者)
与传统的 Worker 不同,Service Worker 是一种特殊类型的 Web Worker,通常用于拦截网络请求、实现离线缓存、消息推送等功能。与传统的 Worker 不同,它的生命周期并不与浏览器页面绑定,而是与整个网站相关联。主要与浏览器的主线程和 Web 页面进行交互。它还可以处理浏览器的网络请求,拦截和缓存请求。运行在浏览器的独立线程中,不依赖于网页的生命周期,即使网页关闭,也可以继续运行(直至被浏览器回收)。
生命周期
- 安装(install):初次注册或更新时触发,可用于缓存静态资源。
- 激活(activate):安装完成后触发,通常用于清理旧缓存或旧版本数据。
- 闲置(idle):在不拦截请求时进入闲置状态。
注册Service Worker
在网页的主线程中,通过
navigator.serviceWorker.register()
方法注册 Service Worker 脚本。注册后,浏览器会下载、安装并激活 Service Worker。
1 |
|
Service Worker 脚本
在安装阶段,可以预缓存一些必要的静态资源,这样即使在离线时也可以快速加载页面。
1 |
|
激活阶段
激活阶段通常用于清理旧的缓存或执行其他更新任务。
1 |
|
拦截网络请求(fetch 事件)
Service Worker 最重要的功能之一就是拦截网络请求,并决定返回缓存数据或网络数据。监听
fetch
事件后,使用event.respondWith()
直接提供一个响应。先查找缓存中是否存在请求的资源,如果存在则返回缓存内容;否则,执行网络请求。
1 |
|
- 进阶用法
1 离线体验
通过上述缓存策略,用户在离线时依然可以加载部分预缓存的页面和资源。开发者还可以扩展逻辑,当网络请求失败时返回离线页面或自定义错误页面。
2 后台同步和推送通知
- 后台同步(Background Sync)
Service Worker 可以在网络恢复后自动同步数据。需要借助 SyncManager API 实现。 - 推送通知(Push Notifications)
结合 Push API 与 Service Worker,可以实现消息推送,即使网页未打开也能收到通知。
3 与页面通信
通过 postMessage
方法,可以在 Service Worker 和网页之间进行双向通信。例如,当缓存更新时通知页面刷新数据。
1 |
|
四、注意事项
- 不支持 DOM 操作
由于 Worker 运行在独立的线程中,它没有访问 DOM、window 对象或 document 对象的权限。如果需要更新 UI,必须通过消息将结果传回主线程,然后在主线程中进行 DOM 操作。 - 同源策略
Worker 脚本必须遵循同源策略(同一协议、域名、端口)。跨域加载 Worker 脚本需要额外配置 CORS。 - 调试困难
由于 Worker 在单独的线程中运行,调试时可能会比主线程稍微复杂一些,但现代浏览器大多已经支持 Worker 的调试功能。 - 数据传输
消息传递过程中的数据会被序列化(通过结构化克隆算法),因此有些数据类型(如函数、DOM 节点等)无法传递。如果数据量特别大,可以考虑使用 Transferable Objects 来避免不必要的复制,提高效率。 - 错误处理
Worker 中发生的错误不会直接抛出到主线程,需要通过onerror
事件监听进行捕获和处理。
五、使用场景
- 计算密集型任务
如复杂数学计算、大量数据处理、图像处理、音视频编解码等,可以将这些任务放在 Worker 中执行,避免页面卡顿。 - 数据处理和预处理
例如在接收到网络数据后进行预处理,然后再传回主线程进行显示。 - 离线缓存和网络拦截
通过 Service Worker 实现离线访问、网络请求缓存、后台数据同步等功能。 - 多窗口/标签页共享数据
共享 Worker 可以在多个页面之间共享数据和状态,实现进程间通信。