web worker

为什么要用 worker

因为 javascript 采用的是单线程模型,也就是所有的任务都只能在一个线程上执行,当解析到一个运算量较大的 js 的时候,后面的任务只能等前面的js解析完才能进行其他操作。所以就会出现”卡死“的状态。
Web Worker 它的作用就是解决以上问题。它可以通过加载一个脚本文件,进而创建一个独立工作的线程,在主线程之外运行。从而营造一个多线程的运行环境,充分利用 CPU 的资源,减轻主线程的负担。


worker 中的限制

同源策略

分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

DOM 限制

Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用 document、window、parent 这些对象。但是,Worker 中可以用 self 代替 window 对象,此外,还可以使用 navigator 对象和 location 对象。

通信联系

Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

脚本限制

Worker 线程不能执行 alert() 方法和 confirm() 方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

文件限制

Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本必须来自网络。


worker 的使用

主线程与 worker 线程间的通信依赖以下两个方法:

推送消息:postMessage

接收消息:onmessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// worker.js
self.onmessage = event => {
console.log(event.data); // {name:'silence', age:20}
postMessage("close worker");
self.close();
}


// index.js
const worker = new Worker("./worker.js");
worker.postMessage({
name: "Silence",
age: 20
});
worker.onmessage = event => {
console.log(event.data);
// 关闭 worker
worker.terminate();
}
加载脚本
1
2
// worker.js
importScripts("script1.js", "script2.js")
运行

worker 文件需要通过服务器来访问,因此使用 Worker 的项目一般要在 httpServer 中运行。


vue 中使用 Worker

webpack 4

vue 中如果直接使用 worker,首先遇到的是 worker 文件的路径和打包解析的问题。

方案一:直接将 worker 文件放到静态文件夹下(public),通过绝对路径引用。但是这样的话就不能在 worker 文件中通过 import、require 引入其他文件的代码。

方案二:使用 worker-loader。worker 文件不仅可以放在任意地方,还可以在文件中使用 import 和 require 引入其他代码。

worker-loader
安装
1
npm install worker-loader
配置

内联方式:

1
2
3
import Worker from "worker-loader!./Worker.js";

const worker = new Worker();

配置文件方式:

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
chainWebpack(config) {
config.module.rule('js').exclude.add(/\.worker\.js$/);

config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.options({
inline: "fallback"
})
.end();

config.output.globalObject('this')
},
parallel: false
}
1
2
3
import Worker from "./file.worker.js";

const worker = new Worker();

webapck 5

如果是 webpack 5 的话,不需要 worker-loader 也可以正常使用了。

语法
1
new Worker(new URL('./worker.js', import.meta.url));

详情请见 https://webpack.js.org/guides/web-workers/。