文章目录
为什么要用到worker
因为javascript采用的是单线程模型,也就是所有的任务都只能在一个线程上执行,当解析到一个运算量较大的js的时候,后面的任务只能等前面的js解析完才能进行其他操作。所以就会出现“卡死“的状态。
Web Worker它的作用就是解决以上问题。它可以通过加载一个脚本文件,进而创建一个独立工作的线程,在主线程之外运行。从而营造一个多线程的运行环境,充分利用CPU的资源,减轻主线程的负担。
worker中的限制
(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络
线程间通信
postMessage:推送信息
onmessage:接收信息
//Worker线程(test.js)
onmessage = e => {
console.log(e.data) // you see you!
postMessage('one day day!')
}
// 主线程
let w = new Worker('test.js') // 建立Worker
w.postMessage('you see you!')
w.onmessage = e => {
console.log(e.data) // one day day!
w.terminate() // 关闭Worker
}
注意:postMssage只能传递一个参数,传递多个会报错,多个参数建议用Object来传递。
关闭Worker
在使用完worker之后,记得及时关闭,节省系统的资源。
// 主线程
worker.terminate();
// Worker 线程
self.close();
加载脚本
importScripts('script1.js', 'script2.js')
本地文件中使用Web Worker
因为Worker有文件限制,所以要想在本地测试的话有两个方法,
1.通过Blob文件传递worker文件
2.通过服务器访问worker文件(推荐)
通过Blob对象实现Worker
需要将JS的实现代码放到script标签中,然后获取script标签的内容转为Blob对象再建立Worker通信。
<body>
<p>Count numbers: <output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button>
<script id="getWorker" type="javascript/worker">
onmessage = e => {
console.log(e.data)
postMessage('发送的信息')
}
</script>
<script>
function test() {
let workerBlob = new Blob([document.getElementById('getWorker').textContent])
let w = new Worker(window.URL.createObjectURL(workerBlob))
w.postMessage('you see you!')
w.onmessage = e => {
console.log(e.data) // one day day!
w.terminate() // 关闭Worker
}
}
</script>
</body>
通过服务器访问worker文件
1.如果你是使用VSCode编译器,直接下载Live Server插件即可
然后点击Go Live直接跑就可以了,无需转为Blob
2.可以利用Browsersync同步测试工具
Browsersync官方文档
Browsersync使用教程
vue中使用Worker
首先需要安装worker-loader来解析worker文件
npm install worker-loader -D
配置vue.config.js文件
// vue-cli3.0+
module.exports = {
configureWebpack: config => {
config.module.rules.push({
test: /\.worker\.(c|m)?js$/i,
use: {
loader: 'worker-loader',
options: {
inline: true,
publicPath: (pathData, assetInfo) => {
return `/scripts/${pathData.hash}/workers/`
}
}
}
})
}
}
引入文件
引入文件不再是new Worker构建函数了,而是直接实例化该文件!
// test.worker.js
import WorkerFile from './b.js'
let worker = new WorkerFile()
如果worker中使用到window会报错,则需要添加如下配置
chainWebpack: config => {
config.output.globalObject('this')
}
打包的时候也会报错,还需添加如下配置
parallel: false
完整版vue.config.js配置
// vue-cli3.0+
module.exports = {
configureWebpack: config => {
config.module.rules.push({
test: /\.worker\.(c|m)?js$/i,
use: {
loader: 'worker-loader',
options: {
inline: true,
publicPath: (pathData, assetInfo) => {
return `/scripts/${pathData.hash}/workers/`
}
}
}
})
},
parallel: false,
chainWebpack: config => {
config.output.globalObject('this')
}
}
注意:打包后的worker文件没办法实时刷新,即使配置了生成hash也不行,网上说回退到worker-loader2.0.0版本配置options{name: ‘/scripts/workers/WorkerName.[hash].js’}可以,亲测也是不行!暂时只能手动修改文件名来刷新!
参考链接:
worker文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
阮一峰教程:http://www.ruanyifeng.com/blog/2018/07/web-worker.html
博客园教程:https://www.cnblogs.com/gerry2019/p/11456035.html
webpack文档:https://github.com/webpack-contrib/worker-loader