练习中……

参考:

  1. 阮一峰老师关于web worker的链接: https://www.ruanyifeng.com/blog/2018/07/web-worker.html
  2. https://blog.csdn.net/weixin_42063951/article/details/125300644 (用的webpack,在我的项目中并没有办法使用)
  3. vite官方文档https://cn.vitejs.dev/guide/features#web-workers

webworker注意点

Web Worker 有以下几个使用注意点。

同源限制、DOM限制、通信限制、脚本限制、文件限制

  1. 分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
  2. Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
  3. Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
  4. Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求
  5. Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
  6. 全局变量中并不存在this,this并不指向window,有self,指向worker本身。(os:但是在worker.js中打印出来的this和self显示一致)
  7. 子线程和父线程的通讯是通过值拷贝,子线程对通信内容的修改,不会影响到父线程。在通信过程中值过大也会影响到性能(解决这个问题可以用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’)

报错记录

  1. 依据下方的截图
    在这里插入图片描述
    和从网上搜的帖子我的理解是:Worker的参数不能是本地的文件,但是链接2里边作者的确用了本地的文件来写,但是我的本地按照链接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
  })
  1. 找不到库
    没找到正确引入下载的库的方式
    解决办法:(不知道合不合适…)
把需要的库的min.js文件拿出来引入
self.importScripts("spark-md5.min.js") // 与worker.js文件同级
  1. 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('接收到了')
}
  1. 用相对路径引入其他文件夹中的文件会报错
self.importScripts('../../api/imageManage/uploadImage.js')

在这里插入图片描述
把请求接口写在了work.js中,使用的fetch方法。(后续记录的解决办法,时间有些长,webworker后来在项目中也移除了)
在这里插入图片描述

  1. ;
  2. ;

代码

(一)

从放弃到坚持放弃,使用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)
      })
  })
}
Logo

前往低代码交流专区

更多推荐