03-web worker vue项目实战

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当然也可以),就是得用原生的请求方法。

 有时间再研究吧。。。。

 

posted @ 2021-05-16 01:01  猎奇游渔  阅读(1128)  评论(0编辑  收藏  举报