一、先确认问题:卡的是计算,还是主线程#浏览器的主线程同时负责三件事:执行 JavaScript、样式与布局计算、处理用户输入。它们共享同一个事件循环。一段 200 ms 的同步计算跑起来,页面不会「变慢一点」,而是这段时间里点击没有反馈、动画掉帧、输入框里的字符要等计算结束才出现。Core Web Vitals 里的 INP 量的就是这种延迟。
所以「要不要上 Worker」的判断标准不是「计算复不复杂」,而是:这段计算是否阻塞了主线程,并且它的结果不需要同步返回。两个条件缺一不可。一个必须在点击瞬间同步算出布局位置的 3 ms 计算,放进 Worker 反而更慢;一个 800 ms 的报表导出,即使只跑一次,也值得搬走。
Worker 提供了三种形态,先分清用途:
类型创建方式生命周期适合做什么Dedicated Workernew Worker(url)跟随创建它的页面页面私有的重计算、解析、编解码Shared Workernew SharedWorker(url)同源多个页面共享多标签页共享连接或状态Service Workernavigator.serviceWorker.register()由浏览器管理,随时可能被终止网络代理、离线缓存、推送Service Worker 虽然也跑在独立线程,但它的设计目标是拦截网络请求,浏览器可以在空闲时终止它,不适合承载长计算。本文说的「Worker」默认指 Dedicated Worker,Service Worker 的离线策略见另一篇。
下面按三笔账展开:消息传递、线程开销、共享状态。每一笔都有对应的代码和容易踩的坑。