web worker 网上一大堆讲解,各种互相的复制粘贴,就算讲也是各种不标明版本所对应的配置,断章取义,就算有详细的,也只是在本地的html页面和js中去做的讲解和阐述,那么问题来了,现在基本都用mv**框架吧,就拿vue来说,就没有正儿八经的系统的去描述怎么用的。真是让人头大。。。官方API又说的很简明扼要,需要自己各种尝试。。。
这里只介绍vue项目中想通过新开一个浏览器线程,用于数据量大,请求的接口比较慢的作为应用场景:
因为在html和js中去用很简单,在vue cli中因为涉及到了文件打包,所以需要做配置。
首先,用webpack官方提供的worker-loader这个插件,但是因为又看见了一个叫做vue-worker的插件(是人家封装好的web worker,在vue中可以开箱即用的,不用再在配置文件中去添加一些配置)当然这就很好了,所以我刚开始用的时候作为首选,毕竟怎么简单怎么来嘛~
第一步:安装
npm i vue-worker
第二步:vue中使用
methods方法中去触发一个方法(比如点击事件),把入参发送给woker线程,worker请求接口
testFn() {
this.worker = this.$worker.create([
{
message: "reqData",
// 这个函数是另一个线程的,无法访问外面的任何属性,连window也不行
func: e => {
// console.log(window, "wo shi window"); //window is not defined
console.log("收", e); // 接收到消息之后发送
var getData = JSON.parse(JSON.stringify(e));
delete getData.token;
var url = new URL(process.env.VUE_APP_API_URL + "interviewer");
var params = getData;
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
let { token } = e;
let getInterfaceData = null;
let resData = null;
// 要想用async和await 需要配置babel-loader
async function tempFn() {
// 接口一直在pending,不知道怎么回事
resData = await fetch(url, {
headers: {
Accept: "application/json",
Authorization: "Bearer " + token
}
});
//
let getInterfaceData = await resData.json();
console.log(resData, "dsdas");
}
tempFn();
// 原生ajax也是一直pending
// var xmlhttp = new XMLHttpRequest();
// xmlhttp.open("GET", url, true);
// xmlhttp.setRequestHeader("Authorization", "Bearer " + token);
// xmlhttp.setRequestHeader("Accept", "application/json");
// xmlhttp.send();
// xmlhttp.onreadystatechange = function() {
// if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// console.log(xmlhttp.responseText);
// }
// };
// console.log("试试", e.context);
return getInterfaceData;
}
}
]);
let sendData = {
page: this.currentPage,
per_page: this.pageSize,
type: 0,
is_excellent: this.is_excellent,
is_negative: this.is_negative,
workcode: this.workcode,
token: cookie.get("admin_token")
};
this.worker.postMessage("reqData", [sendData]).then(function(e) {
console.log(e, "我是接口发来的数据");
});
},
效果:
两个问题:
1. func函数里面不能用外面任何属性和引入,所以也就只能用原生fetch 或 ajax开发送接口请求
2.接口请求一直pending,自然无法拿到数据。那还玩个毛?
好吧,既然暂时无法有一个好的解决方法,我们暂时换 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线程中),跟平时调接口一样。
https://webpack.docschina.org/loaders/worker-loader/
问题有三:
1. 刷新当前页面,有时候vue组件拿不到worker线程接口请求传来的数据,切换其他路由再切回来就没问题
2. worker文件中如果有修改,必须重新编译,直接ctrl+s保存不生效,这也是比较纳闷的一个问题,明明在配置的时候filename已经加了hash了。这搞得就跟修改配置文件一般,有点麻烦
3.worker文件中不能直接import xxx from xxx,比如已经封装好的接口直接拿过来直接xxx.then(),否则编译的时候就卡着不动了,现有解决方法就是用fetch(原生的ajax当然也可以),就是得用原生的请求方法。
有时间再研究吧。。。。