• 使用require.context实现优雅的预加载


    前言

    在前端开发中,对页面花里胡哨度[注1]要求越高的页面,用到的图片、音频什么的就越多,比如什么结婚请柬、展会请柬、发布会宣传页、数据大屏。虽然现在浏览器不允许网页在没有用户交互的情况下播放音频,但是,我们依旧要在页面展现的同时,准备好所有的静态资源。

    注1:花里胡哨度(garish degree),又名难做指数,江湖人称领导开心点

    丑陋的预加载

    预加载即提前加载,浏览器在请求一张图片时,会缓存到本地,在下次请求同样的地址时,会直接在本地缓存读取(304),在本地读取的时间基本可以忽略不计。如果我们能够在图片未加载完成时给用户一个加载进度,提示用户:“急什么,马上完事!”,则能够有效的提升用户体验。

    单张预加载

    相信同学都了解图片的预加载:

    let img = new Image()
    img.src = "@/../../xx.png"
    img.onload = () => {
    	//...
    }
    

    这是为大家所熟知的预加载方式,但是这种方法只适用于单张图片的预加载。

    那多张怎么做呢?

    多张预加载

    很简单,我们给图片们定义一个数组就好了

    let imagesPathArr = ["@/../../xx.png","@/../../yy.png","..."];
    

    然后我们再用循环去加载这些图片

    let count = 0        
    for (let item of imagesPathArr) {
              let img = new Image()
              img.src = item
              img.onload = () => {
                count++
                if (count === imagesPathArr.length) {
                    // ... 加载完成
        		}
      		}
    }
    

    我们甚至可以通过count/imagesPathArr.length算出加载的百分比 。

    没错,但是这种方法加载十张图片还可以,那加载一百张呢?

    同学说:“我可以把图片从0-99命名,加载时只需要循环一百次就可以了!”

    可以,那么假如我们用python写了一个重命名脚本,把这一百张图片从0-99命名完成。

    那么我们的代码就长这样:

    for(let i = 0;i<=99;i++){
    	let img = new Image()
          img.src = `@/../../${i}.png`
          img.onload = () => {
          count++
          if (count === imagesPathArr.length) {
              // ... 加载完成
          }
      	}
    }
    

    ok,看起来没有任何问题,实际上也没有任何问题。

    但是在使用过程中,我们会发现,图片的格式不一定是统一的(当然你可以将他们转换成统一的),而且这种方式看起来太丑了,一点也不够优雅。

    那么有没有一种方式,优雅的预加载呢?有。

    优雅的预加载

    要实现优雅的预加载,我们要优哪些方面?

    • 第一,我们无需知道加载的图片有多少;
    • 第二,我们无需知道加载的图片叫什么;
    • 第三,我们无需知道图片的格式是什么。

    他🐎的,这听起来就优雅,相当于什么都不用干,就把预加载做出来了!

    但是,众所周知,浏览器环境没有直接操作文件系统的能力,我们无法像node一样,直接使用fs,怎么才能做到如上所说的呢?从第一步来看,我们至少要遍历一个父级文件夹吧?

    本期的主角登场

    require.context

    它是一个webpack的api,可以通过这个方法获取一个特定的上下文,用来实现文件的批量自动化导入,如果你使用vite,那么可以使用 import.meta.globEager(),本文只用require.context举例。

    好像这个api已经存在了好久了,但是我是最近才知道的😅,在这里分享给还没用过的同学。

    使用语法如下:

            let requireModule = require.context(
              "../../../public/static/img", // 需要遍历的路径
              false, // 是否递归,设置为true会递归到最后一级文件夹
              /\.png|\.webp|\.jpg|\.jpeg|\.bmp|\.gif$/ //匹配的正则表达式
            );
    

    上述代码匹配了常用的图片格式。

    如果我们循环它的key(),会得到类似./xxx.png的项,所以,只要去掉./就得到了文件夹下所有的图片。

    所以,我们可以做一个数组来储存所有的图片路径:

    let imagesPathArr = [];
    for (var i = 0; i < requireModule.keys().length; i++) {
        imagesPathArr.push("/static/img/" + requireModule.keys()[i].substr(2, requireModule.keys()[i].length));
    }
    

    这样,imagesPathArr就拥有了我们指定文件夹下所有的图片路径了,我们根本无需关心图片有多少、叫什么、什么格式。

    下面直接对imagesPathArr进行循环(跟上面一样),导入所有图片:

            let count = 0
            for (let item of imagesPathArr) {
              let img = new Image()
              img.src = item
              img.onload = () => {
                count++
                if (count === imagesPathArr.length) {
                    // 加载完成
                }
              }
            }
    

    最后,我们把所有的逻辑封装成一个函数,并给他套上promise

        async loadImgs() {
          await new Promise((resolve, reject) => {
            this.$store.dispatch('loadingStart', {
              text: "正在加载资源"
            })
            let requireModule = require.context(
              "../../../public/static/img",
              false,
              /\.png|\.webp|\.jpg|\.jpeg|\.bmp|\.gif$/
            );
            let imagesPathArr = [];
            for (var i = 0; i < requireModule.keys().length; i++) {
              imagesPathArr.push("/static/img/" + requireModule.keys()[i].substr(2, requireModule.keys()[i].length));
            }
            let count = 0
            for (let item of imagesPathArr) {
              let img = new Image()
              img.src = item
              img.onload = () => {
                count++
                if (count === imagesPathArr.length) {
                  this.$store.dispatch('loadingDone')
                  resolve()
                }
              }
            }
          })
        },
    

    我们只需在合适的时机,调用该函数,即可全自动的预加载图片了,而且日后往文件夹内新增或者删除图片,都不用管这一段逻辑,它依然可以稳健运行!如果你有加载音频的需求,也是同理,在正则部分加一个.mp3什么的,使用audio.onload即可!

  • 相关阅读:
    P02014186陈镐镐
    冰狐智能辅助相对autojs的优势
    性能测试:工具篇:Jmeter实时可视化平台搭建
    组件——组件名、非单文件组件、单文件组件
    架构师社区爆火的分布式微服务神仙笔记究竟有什么魅力?
    偷偷盘点一下这几年秋招薪资的变化
    VCS编译时如何加载shared library(.so)库
    idea插件推荐
    rtp流广播吸顶喇叭网络有源吸顶喇叭
    家用工作站方案:ThinkBook 14 2023 版
  • 原文地址:https://www.cnblogs.com/Kay-Larry/p/17374533.html