ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue实际项目中关于图片的大量加载的优化及思路_liuqing0.0的博客

2022-05-18 01:02:25  阅读:612  来源: 互联网

标签:retData vue 请求 线程 liuqing0.0 let data 加载


前言: 项目中的问题首先是没有缓存时进入页面时,点击切换等功能造成的, 这个时候谷歌浏览器中的network中会无缓存请求图片所在的地址,短则1s 长则2s,对用户感觉不是很友好。于是乎第一种解决方法出来了:图片预加载,尽管它仍然有局限性;

图片预加载

let {radarImgArray,radarSource,cloudImgArray,cloudSource} = radarAndCloudObj
        let images = new Array();
        for (let i = radarImgArray.length - 1; i >= 0; i--) {
          images[i] = new Image();
          images[i].src = radarSource + radarImgArray[i];
          images[i].onload = () => {
            if (i == 19) {
              this.drawradarToMap(this.radarIndex);
            }
          };
        }
        for(let i = 0; i < cloudImgArray.length-1;i++){
          images[i] = new Image();
          images[i].src = cloudSource + cloudImgArray[i];
          images[i].onload = () => {
            // console.log(name +'=========>' + i);
            if (i == 19) {
              this.mess.close();
              const h = this.$createElement;
              Message({
                message: h("p", null, [
                  h("span", { style: "color:#42f0ff" }, "数据加载完成 "),
                ]),
                duration: 3000,
                offset: 150,
                type: "success",
                customClass: "messageClass",
              });
            }
          };
        }

好了,上面的代码不用看那么多,讲述一下思路,在你创建一个Image对象并赋予这个image对象的src属性的时候,你可以查看一下network,你会发现他一直在加载。每一张加载完之后都会调用一个onload回调,我们可以在这里面做一些事。

  • 但同时你应该也已经发现了:由于使用的是for循环,一旦轮到这个函数实现的时候,异步队列中顺延下去,我的总共是40张图片需要在一开始加载,这会你在别的地方调用网络请求的时候,你会发现怎么也请求不到,原因很简单,排上了队,但是没轮到它;

交代完前情,我开始考虑第二种去实现这个图片加载的方法,这里想要解决的痛点只有一个,不管他加载与否,只要我想调用别的网络请求就能顺畅响应,一句话来说:我想让我在操作的时候的优先级是最高的

第一时间想到线程相关,假如我可以一个线程去跑这个加载的任务,不会影响到我主线程的任何操作,可是js是单线程的语言,怎么实现多线程的操作呢?一顿搜索:使用web worker

web workers 介绍

在这里插入图片描述
关注上面重点,使用web workers 可以独立于主线程做到运行一个脚本。 注意:worker将运行在于当前window不同的另一个全局上下文中,意味着:你不应该尝试在这里面去操作dom元素,同时Image对象在这里也是找不到的。因为它依赖于window 对象。

照这么解释,使用web workers实现图片预加载这条路就行不通了?

不,可以!但我们也许得试着不从new Image,设置src方向入手。总之我们的核心就是让这个图片在子线程中请求,一般请求我们都是使用ajax,那试试吧。

首先先处理兼容问题,假如用户浏览器版本太低级,你可以使用第一种,反正我是写了两种。
在这里插入图片描述

let worker;
if(Worker !== "undefined"){
	if(typeof worker == "undefined"){
		worker = new Worker("js在本地的路径");
	}
}


上面就完成了如下事情: 独立于主线程运行一个脚本

接着 通过ajax 请求 地址,把它返回的类型设置为blob类型;
在这里插入图片描述
参考代码:

/**
 * 这个js 文件 主要用于 给主线程  跑这 一个 极其需要使用浏览器资源的图片预加载
 * preloadByRadar.js
 */

onmessage = function (radarAndCloudObj) {
    let data = radarAndCloudObj.data;
    let radarUrlArray = []
    for (let i = data.radarImgArray.length - 1; i >= 0; i--) {
        let req = new XMLHttpRequest();
        let image = data.radarSource + data.radarImgArray[i];
        req.open('GET', image, false);
        req.responseType = 'blob';
        req.onreadystatechange = function () {
            if (req.readyState == 4) {
                radarUrlArray.push(req.response);
                self.postMessage(req.response);
                if (i == 19) {
                    self.postMessage('loadRadarToMap');
                }
            }
        }
        req.send(null);
    }
    self.postMessage([radarUrlArray,'radar']);
}

这里ajax我使用了同步请求,没啥,业务需求,异步怕自己把握不住。 将请求的响应类型设置为blob,然后将请求图片得到的二进制流发送至主线程。

onmessage(callback)

用于接收
简单解释下:接收消息,在子线程中onmessage 接收主线程的postMessage 发送过来的消息,反之,主线程worker的onmessage也可以接受到子线程发送的消息。

postmessage()

在这里插入图片描述
第一个参数是对象类型的,可以被发送到线程的。

主线程中的函数调用 项目中完整的调用例子

/**
     * 雷达图 和 云图的 预加载
     * @param {Object} radarAndCloudObj 顾名思义,包含雷达相关属性 跟云图相关属性的对象
     */
    radarAndCloudImgPreload: function (radarAndCloudObj) {
      let radarWorker, cloudWorker;
      let _this = this;
      // 如果用户的浏览器 版本 支持 worker 就使用更好的方式 去执行预加载
      if (typeof Worker !== "undefined") {
        // 这里也许未来 还可以做 优化、  就是在开启一个线程 分别加载
        // 由于我现在使用的方法是同步的ajax 请求 、 其实并不够完美、
        // 应该是: 一个线程加载雷达图  一个线程加载云图 这样子 应该性能会更卓越。
        //后面测过了,实际上差不多。
        // 当然 日后有空  可以进行修改。
        if (typeof radarWorker == "undefined") {
          radarWorker = new Worker("js/preload/preloadByRadar.js");
          cloudWorker = new Worker("js/preload/preloadByCloud.js");
        }
        // 给子线程发送 数据
        radarWorker.postMessage(radarAndCloudObj);
        cloudWorker.postMessage(radarAndCloudObj);
        //  创建 读取二进制流后的url
        var createObjectURL = function (blob) {
          return window[window.webkitURL ? "webkitURL" : "URL"][
            "createObjectURL"
          ](blob);
        };

        // 监听雷达图 子线程发送的消息
        radarWorker.onmessage = function (status) {
          let retData = status.data;
          if (retData == "loadRadarToMap") {
            // 这里我觉得 不应该去做改动
            // 原因如下: 更优秀的编码的价值 不高于修改的价值。
            // 一句话来说 不够优雅~
            _this.drawradarToMap(_this.radarIndex);
          } else if (retData instanceof Array && retData.length == 2) {
            // 处理bloburls  将原本通过拼接请求地址的数组 转换成已经被处理过的请求数组
            _this.blobRadarUrls = retData[0];
            _this.blobRadarUrls.map((item, index, arr) => {
              var newimgdata = createObjectURL(item);
              arr[index] = newimgdata;
            });
            console.log(retData);
          }
        };

        // 监听云图 子线程发送的消息
        cloudWorker.onmessage = function (status) {
          let retData = status.data;
          if (retData == "allPictureInit") {
            // 全部加载完了 ,将原本的提示信息 关闭 , 同时提示数据加载完成
            _this.mess.close();
            const h = _this.$createElement;
            Message({
              message: h("p", null, [
                h("span", { style: "color:#42f0ff" }, "数据加载完成 "),
              ]),
              duration: 3000,
              offset: 150,
              type: "success",
              customClass: "messageClass",
            });
          } else if (retData instanceof Array && retData.length == 2) {
            // 处理bloburls  将原本通过拼接请求地址的数组 转换成已经被处理过的请求数组
            _this.blobCloudUrls = retData[0];
            _this.blobCloudUrls.map((item, index, arr) => {
              var newimgdata = createObjectURL(item);
              arr[index] = newimgdata;
            });
            // 表示数组已经转换完毕 我们下次点击之后 就使用该数组
            _this.blobInit = true;
          }
        };
      }
   }

let radarAndCloudObj = {
        radarImgArray: this.radarImages,
        radarSource: "/api/data/radarpic/",
        cloudImgArray: this.cloudImgArray,
        cloudSource: "/api/data/cloud/IR1/",
      };

讲述下上面代码的核心逻辑:
1、首先主线程运行一个独立于主线程的脚本;
2、接着给这个脚本(子线程)发送数据,我这里后端给我返回的是图片地址的后缀,我得把前面的路径也一起发送过去。
3、把子线程中ajax请求后发送的消息监听到,一个arr,长度为2,当然这里你也可以自己去修改,重点是监听到这个图片数据已经请求完了。
4、将设置在组件中的bloburls 处理,请求的是二进制流,我们需要的是一个可供我们展示的地址
在这里插入图片描述
在这里插入图片描述
5、当全部加载完后,发送一个字符串"allPictureInit"表示加载完成。同时把blobInit设置为true,表示通过我们请求的二进制流转换成的与当前页面相关的domString已经全部可以使用(ps: 你也可以每监听到一张图片被加载,就转换原本的请求地址。看实际需求)。

调用实例

// 将设置 imgsource 的公共逻辑抽离出来
    changeCloudImgSource: function (index) {
      this.cloudImg = this.blobInit
        ? this.blobCloudUrls[index]
        : "http://x.x.x.x/data/cloud/IR1/" + this.cloudImgArray[index];
    },

效果

第一张图片中展示的是:主线程 是 独立于子线程的,无论我在主线程做什么操作,他都会以主线程为主,子线程只有在主线程空闲时才会进行加载。
在这里插入图片描述
第二张图片展示的是: 使用这个经过二进制流转换的url,加载是近乎不需要任何时间的。
在这里插入图片描述

本文转自 https://blog.csdn.net/q1025387665a/article/details/118210111?spm=1001.2014.3001.5502,如有侵权,请联系删除。

标签:retData,vue,请求,线程,liuqing0.0,let,data,加载
来源: https://www.cnblogs.com/hustshu/p/16283078.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有