使用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即可!
使用require.context实现优雅的预加载的更多相关文章
- iOS教程:如何使用Core Data – 预加载和引入数据
这是接着上一次<iOS教程:Core Data数据持久性存储基础教程>的后续教程,程序也会使用上一次制作完成的. 再上一个教程中,我们只做了一个数据模型,之后我们使用这个数据模型中的数据创 ...
- Javascript图片预加载详解
预加载图片是提高用户体验的一个很好方法.图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度.这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速.无缝地发布 ...
- ASP.NET MVC3 Razor 调试与预加载
目录(?)[-] 获取服务器信息 FormsAuthenticationSlidingExpiration 属性 MVC3预加载 在ASP.NET MVC3开发中,调试中怎么也是不可缺少的,那对于 ...
- entity framework 数据加载三种方式的异同(延迟加载,预加载,显示加载)
三种加载方式的区别 显示加载: 显示加载
- Entity Framework关联查询以及数据加载(延迟加载,预加载)
数据加载分为延迟加载和预加载 EF的关联实体加载有三种方式:Lazy Loading,Eager Loading,Explicit Loading,其中Lazy Loading和Explicit Lo ...
- Angular.JS + Require.JS + angular-async-loader 来实现异步加载 angular 模块
传统的 angular 应用不支持异步加载模块,必须在 module 启动的时候,所有模块必须预加载进来. 通过使用 angular-async-loader 库,我们可以使用 requirejs 等 ...
- ViewPager的刷新、限制预加载、缓存所有
[框架]: 公共部分:左侧菜单.TitleBar.RadioGroup(3个RadioButton:X.Y.Z) 选择X页面:指示器+ViewPager [要达成的效果]: (1)左侧选择A,进入X页 ...
- 关于禁止ViewPager预加载问题【转】
转自:http://blog.csdn.net/qq_21898059/article/details/51453938#comments 我最近上班又遇到一个小难题了,就是如题所述:ViewPage ...
- ViewPager防止Fragment销毁以及取消Fragment的预加载
存在的问题 1. 默认情况下,ViewPager会根据setOffscreenPageLimit()方法设置的大小,自动预加载2. 还是根据setOffscreenPageLimit()方法设置的大小 ...
- 基于spring的web项目启动时预加载数据到ServletContext
1.要在web启动时预加载数据到ServletContext,实现方法有很多,一种比较简单的方案就是: 1)新建一个bean,定义其初始化方法: <bean id="beanId&qu ...
随机推荐
- vue再请求头加token
Vue:全局拦截所有请求,并在请求头中添加token - 道祖且长 - 博客园 (cnblogs.com) util.js export function sessionSet(user){ let ...
- 记一次Centos7上安装VNC服务
需要部署oracle数据库,操作系统为Centos7.5,oracle数据库在linux上面部署必须要安装一些依赖包,安装好,当然可以通过静默化安装,时间紧任务重,就通过vnc服务来进行安装,桌面化操 ...
- 在Vue的mixins(混入)里面调用Vuex(@/store/index.js)的函数
第一步:在mixin.js里面引入 mapMutations 第二步:跟组件内调用一样,在methods里面写 "...mapMutations(['xxx'])", 然后LZ ...
- ARFoundation在2019.2之后无法打包的问题
打包提示错误gradle无法完成打包.解决方案 转到首选项>外部工具> Android> Gradle ,然后将自定义Gradle设置为Gradle 5.6.4或更高版本.请参阅Gr ...
- ssh反向代理树莓派+motion,实现公网远程视频监控
注意:本文公网远程监控部分需要借助有公网IP的云服务器进行ssh反向代理. 一.借助motion实现内网的视频监控 准备 插上摄像头,然后输入ls /dev/video*命令检查是否识别了摄像头 安装 ...
- U-boot分析之编译体验
1. 如图可以看到的是PC和嵌入式系统的区别 PC第一步是上电,进入BIOS而嵌入式系统进入bootloader 接着进入操作系统而嵌入式式进入Linux内核 PC识别C盘,D盘,而嵌入式系统则挂载根 ...
- docker学习随笔
总结自https://zhuanlan.zhihu.com/p/187505981 Linux内核提供了Namespace技术来隔离PID/IPC/网络资源等,还提供了Control Group(cg ...
- [jointjs] 自定义shape
前面一篇写了使用jointjs实现自动布局和拖拽缩放,这篇记录一下自定义图形. 首先jointjs内置的图形有很多,文档已经列出来了: 但是有时候这些图形满足不了我们的需求,就需要我们自己去绘制自己想 ...
- MarkDown学习day1
# Markdown学习 ## 标题: #+"空格"+标题名字 为一级为标题 ##+"空格"+标题名字 为二级标题 同理几级标题就是几个#,最多支持6级标题 # ...
- adb 全局
win10: 我的电脑-右键属性--系统保护--高级--环境变量--选择path--编辑--点击新建 在新建条目下输入 C:\Users\GL\platform-tool--重新打开cmd 测试adb ...