web worker 网上一大堆讲解,各种互相的复制粘贴,就算讲也是各种不标明版本所对应的配置,断章取义,就算有详细的,也只是在本地的html页面和js中去做的讲解和阐述,那么问题来了,现在基本都用mv**框架吧,就拿vue来说,就没有正儿八经的系统的去描述怎么用的。真是让人头大。。。官方API又说的很简明扼要,需要自己各种尝试。。。
这里只介绍vue项目中想通过新开一个浏览器线程,用于数据量大,请求的接口比较慢的作为应用场景:
因为在html和js中去用很简单,在vue cli中因为涉及到了文件打包,所以需要做配置。
首先,用webpack官方提供的worker-loader这个插件,但是因为又看见了一个叫做vue-worker的插件(是人家封装好的web worker,在vue中可以开箱即用的,不用再在配置文件中去添加一些配置)当然这就很好了,所以我刚开始用的时候作为首选,毕竟怎么简单怎么来嘛~
好吧,既然暂时无法有一个好的解决方法,我们暂时换 worker-loader上场为我们服务:
(此案例是调用了一个列表页面的接口用于测试)
第一步:安装
npm i worker-loader (项目依赖)
第二步:vue.config.js配置
chainWebpack: config => { // 配置 config.module .rule("worker") // .test(/\.worker\.js$/) .test(/\.worker\.(c|m)?js$/i) .use("worker") .loader("worker-loader") .options({ inline: "fallback", filename: "[name].[contenthash].worker.js" }) .end(); // 解决 "window is undefined", 这是因为 worker 线程中不存在 window 对象, 要用 this 代替 config.output.globalObject("this"); }
第三步:在vue组件中引入worker文件
// 引入存放worker的路径即可(注意,命名必須是xxx.worker.js結尾) import myWorker1 from "@/worker/my1.worker.js";
第四步:在vue組件中的methods中创建实例,然后在created中去调用(初始化)
getList() { // 创建引入的worker实例 let worker = new myWorker1(); // 组织worker中接口需要的数据传递给worker const params = { page: this.currentPage, per_page: this.pageSize, type: 0, is_excellent: this.is_excellent, is_negative: this.is_negative, workcode: this.workcode, // 把登录后拿到的token传过去 token: cookie.get("admin_token") }; //向工作线程发送消息 worker.postMessage(params); setTimeout(() => { worker.onmessage = event => { //主线程接收到工作线程的消息 console.log(event.data.data, "主线程接收"); // 拿到worker线程请求好的数据,给页面绑定数据 let getWorkerData = event.data.data; if (getWorkerData) { this.tableData = getWorkerData.list; this.totalCount = getWorkerData.total; this.loading = false; } //关闭线程 worker.terminate(); }; }); worker.onerror = function(error) { console.log(‘错误信息‘, error, error.message); worker.terminate(); }; }
第五步:创建worker文件
// 请求的接口 var url = new URL(process.env.VUE_APP_API_URL + "interviewer"); onmessage = async function(event) { // 克隆传来的数据,传来的接口数据中并不需要token,token只是用作鉴权用,拷贝后删掉token属性 let cloneObj = JSON.parse(JSON.stringify(event.data)); delete cloneObj.token; let params = cloneObj; // 把传来的对象格式序列化成查询字符串 ?key=value&xxx=xx,并拼在请求路径的后面,fetch文档并没有对get请求入参的api,需要自己处理 Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); //工作线程接收到主线程的消息 console.log(event.data, "工作线程接收"); // 发送fetch请求,并携带此项目需要的headers头部信息 let res = await fetch(url, { headers: { Accept: "application/json", Authorization: "Bearer " + event.data.token } }); let data = await res.json(); if (data) { console.log(data, "接口数据"); // 传给主线程 postMessage(data); } }; //错误信息 onerror = function(event) { console.log(event.message); };
效果:
谷歌浏览器worker请求的接口的response是空的:
火狐有:
至此,列表页中的接口请求就放在worker中去请求了,vue组件中只负责传递接口需要的参数,比如搜索,组织不同的数据过去,worker就会得到不同的入参去重新请求,然后再把请求结果传递给vue组件中(JS线程中),跟平时调接口一样。
问题有三:
1. 刷新当前页面,有时候vue组件拿不到worker线程接口请求传来的数据,切换其他路由再切回来就没问题
2. worker文件中如果有修改,必须重新编译,直接ctrl+s保存不生效,这也是比较纳闷的一个问题,明明在配置的时候filename已经加了hash了。这搞得就跟修改配置文件一般,有点麻烦
3.worker文件中不能直接import xxx from xxx,比如已经封装好的接口直接拿过来直接xxx.then(),否则编译的时候就卡着不动了,现有解决方法就是用fetch(原生的ajax当然也可以),就是得用原生的请求方法。
有时间再研究吧。。。。