vite + vue3 + web worker使用记录
vue3使用web worker
·
练习中……
参考:
- 阮一峰老师关于web worker的链接: https://www.ruanyifeng.com/blog/2018/07/web-worker.html
- https://blog.csdn.net/weixin_42063951/article/details/125300644 (用的webpack,在我的项目中并没有办法使用)
- vite官方文档https://cn.vitejs.dev/guide/features#web-workers
webworker注意点
Web Worker 有以下几个使用注意点。
同源限制、DOM限制、通信限制、脚本限制、文件限制
- 分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
- Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
- Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
- Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求
- Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
- 全局变量中并不存在this,this并不指向window,有self,指向worker本身。(os:但是在worker.js中打印出来的this和self显示一致)
- 子线程和父线程的通讯是通过值拷贝,子线程对通信内容的修改,不会影响到父线程。在通信过程中值过大也会影响到性能(解决这个问题可以用transferable objects)
通信方法
(1) 发送消息
主线程:worker.postMessage();
子线程:self.postMessage()
(2)接收消息
主线程:worker.onmessage(); // addEventListener(‘message’)
子线程:self.onmessage()
(3)监听异常
主线程:worker.onerror();
子线程:self.onerror()
(4)销毁方法
主线程:worker.terminate();
子线程:self.close()
(5)加载脚本(worker内部加载其他脚本)
importScripts(‘one.js’, ‘two.js’)
报错记录
- 依据下方的截图
和从网上搜的帖子我的理解是:Worker的参数不能是本地的文件,但是链接2里边作者的确用了本地的文件来写,但是我的本地按照链接2来写会报: - Vite is unable to parse the worker options as the value is not static.To ignore this error, please use / @vite-ignore / in the worker options.
Vite无法解析工作者选项,因为该值不是静态的。要忽略此错误,请在worker选项中使用/* @vite-ignore */。
第二个参数是可选的配置对象,可以指定 type、credentials、name 三个属性
let worker = new Worker(new URL('work.js', import.meta.url), /* @vite-ignore */{
name: file.name
})
没找到正确引入下载的库的方式
解决办法:(不知道合不合适…)
把需要的库的min.js文件拿出来引入
self.importScripts("spark-md5.min.js") // 与worker.js文件同级
- Uncaught (in promise) DOMException: Failed to execute ‘postMessage’ on ‘Worker’: Value at index 0 does not have a transferable type
翻译:未捕获(在promise中)DOMException:在’Worker’上执行’postMessage’失败:索引0的值没有可转移类型
错误代码:
let worker = new Worker(new URL('work.js', import.meta.url), /* @vite-ignore */{ name: file.uid })
let workerFile = file
worker.postMessage(workerFile, [workerFile])
没搞懂Transferable Objects到底咋用(但反正是传过去了,应该要用ArrayBuffer格式的)……
vue文件
let reader = new FileReader()
reader.readAsArrayBuffer(file) //
reader.onload = (e)=> {
let worker = new Worker(new URL('work.js', import.meta.url), /* @vite-ignore */{ name: file.name
})
let buffer = e.target.result
let workerFile = {
buffer,
name: file.name,
uid: file.uid
}
worker.postMessage(workerFile, [workerFile.buffer]) // 在这之后再打印file里边的属性是找不到的,因为该文件已经转移了
worker.onmessage = function (e) {
console.log(e)
}
}
work.js
var self = this
self.onmessage = async val => {
const { data } = val
let file = new File([data.buffer], data.name) // 因为接下来要用到File文件,所以这个地方要转成File对象
// 返回数据给调用者
// postMessage('接收到了')
}
- 用相对路径引入其他文件夹中的文件会报错
self.importScripts('../../api/imageManage/uploadImage.js')
把请求接口写在了work.js中,使用的fetch方法。(后续记录的解决办法,时间有些长,webworker后来在项目中也移除了)
- ;
- ;
代码
(一)
从放弃到坚持放弃,使用vite的方式会成功通信(只是打包后会报Uncaught TypeError: Cannot read properties of undefined (reading: ‘importScripts’),跟打包有关的我就不懂了。不知道项目配置不一样会不会报此错误):
文件目录:
roleList.vue代码:
<script setup>
const worker = new Worker(new URL('./worker.js', import.meta.url)) // 此方式在生产环境可能会导致报错
worker.postMessage('aaaaaaa')
worker.onmessage = e => {
console.log('来自worker的数据:' + e.data)
}
</script>
worker.js代码:
self.importScripts('spark-md5') //
self.onmessage = function (e) { //监听主线程发过来的消息
console.log('我是worker接收到的数据:' + e.data);
let sum = 0;
self.postMessage(sum); // 将信息发送到主线程上
}
打印如下:
(二)
生产环境中不报错的:
import ImageWorker from "./work.js?worker" // 此方式引入,解决生产环境的报错
const worker = new Worker()
worker.postMessage('aaaaaaa')
worker.onmessage = e => {
console.log('来自worker的数据:' + e.data)
}
</script>
worker.js代码:
import SparkMD5 from 'spark-md5' //
self.onmessage = function (e) { //监听主线程发过来的消息
console.log('我是worker接收到的数据:' + e.data);
let sum = 0;
self.postMessage(sum); // 将信息发送到主线程上
}```
## worker中的请求
因为worker中好像只能使用XMLHttpRequest和fetch,且没有办法引入项目中的api文件(可能是自己没找到合适的方法)。需要在worker.js中重新写一边请求路径。但是不同环境下的ip不一致,没办法写死,遂写了个demo如下:
```javascript
vue文件:
let workerFile = {
metaEnv: import.meta.env.VITE_APP_BASE_API, // 每次都传ip及token
token: 'Bearer ' + getToken(),
buffer,
name: file.name,
uid: file.uid,
fileTableIndex
}
worker.postMessage(workerFile, [workerFile.buffer])
worker.js
self.metaEnv = null
self.token = null
self.baseURL = null
self.fetchRequest = async (url, data) => {
self.baseURL =
location.protocol +
'//' +
location.hostname +
':' +
location.port +
self.metaEnv // 从roleList.vue中通信传过来的
const headers = { Authorization: self.token } // 设置token
if (!(data instanceof FormData)) {
headers['Content-Type'] = 'application/json;charset=utf-8' // 不是formData格式的数据用'application/json;charset=utf-8',formData好像不用特意设置content-type
}
return new Promise((resolve, reject) => {
fetch(self.baseURL + url, {
method: 'post',
body: data instanceof FormData ? data : JSON.stringify(data),// 如果是formData格式直接把data传过去,否则转化一下
headers
})
.then(res => res.json()) // => 为了获取 JSON 的内容,我们需要使用 json() 方法(该方法返回一个将响应 body 解析成 JSON 的 promise)
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
更多推荐
已为社区贡献4条内容
所有评论(0)