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當然也可以),就是得用原生的請求方法。
有時間再研究吧。。。。