webpack 动态批量加载文件的实现方法


Posted in Javascript onMarch 19, 2020

背景

最近笔者在工作中遇到了一个小需求:

要实现一个组件来播放帧图片

这个需求本身不复杂,但是需要在组件中一次性引入十张图片,就像下面这样:

// 就是这么任性,下标从0开始~
import frame0 from './assets/frame_0.png'
import frame1 from './assets/frame_1.png'
import frame2 from './assets/frame_2.png'
// ..省略n张
import frame7 from './assets/frame_8.png'
import frame8 from './assets/frame_9.png'
import frame9 from './assets/frame_10.png'

作为一个有代码洁癖的程序员,我是不允许这种重复性代码存在滴,于是乎就尝试有没有什么简单的方法。

方法一:绕过 webpack

由于笔者用的是 vue-cli 3,熟悉的小伙伴都知道,将图片以固定的格式放在 public 文件夹下面,然后在代码中直接以绝对路径引入即可。这么做的话,就可以根据文件名构造一个 url 数组,简单代码如下:

const frames = []
_.times(10, v => {
  frames.push(`/images/frame_${v}.png`)
})
// 然后你就得到 10个 url 的数组啦

此方法本身是 vue-cli 提供的一个 应急手段,它有几个缺点:

  • 无法利用 webpack 处理资源,无法产生内容哈希,不利于缓存更新
  • 无法利用 url-loader 将资源内联成 base64 字符串 以减少网络请求

方法二:require

由于 import 是静态关键字,所以如果想要批量加载文件,可以使用 require,但是直接像下面这样写是不行的:

const frames = []
_.times(10, v => {
  const path = `./assets/images/frame_${v}.png`
  frames.push(require(path))
}

上面的代码中的 path 是在程序运行时才能确定的,即属于 runtime 阶段,而 webpack 中的 require 是在构建阶段确定文件位置的,所以 webpack 没法推测出这个 path 在哪里。

但是却可以这样写:

const frames = []
_.times(10, v => {
  frames.push(require(`./assets/images/frame_${v}.png`))
}
// frames 中就得到 带 hash 值的路径

虽然这两种写法在语法上没有差别,但是第二种写法在构建时提示了 webpack,webpack 会将 ./assets/images 中的所有文件都加入到 bundle 中,从而在你运行时可以找到对应的文件。

在使用方法二的时候笔者尝试将批量加载的逻辑提取到其他模块用来复用:

export function loadAll (n, prefix, suffix) {
 const frames = []
 _.times(n, v => {
  frames.push(require('./' + prefix + v + suffix))
 })
 return frames
}

但是显然失败了,因为提取后的代码,运行的 context 属于另一个模块,所以也就无法找到相对路径中的文件。

方法三:require.context

上面两种方法都不算很优雅,于是就去翻 webpack 的文档,终于,让我找到了这么一个方法:require.context

require.context(
 directory: String,
 includeSubdirs: Boolean /* 可选的,默认值是 true */,
 filter: RegExp /* 可选的,默认值是 /^\.\/.*$/,所有文件 */,
 mode: String /* 可选的,'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认值是 'sync' */
)

指定一系列完整的依赖关系,通过一个 directory 路径、一个 includeSubdirs 选项、一个 filter 更细粒度的控制模块引入和一个 mode 定义加载方式。然后可以很容易地解析模块.

我们还是看上面的例子:

const frames = []
const context = require.context('./assets/images', false, /frame_\d+.png/)
context.keys().forEach(k => {
  frames.push(context(k))
})

这里的代码通过 require.context 创建了一个 require 上下文。

  • 第一个参数指定了需要加载的文件夹,即组件当前目录下的 ./assets/images 文件夹
  • 第二个参数指定是否需要包含子目录,由于没有子目录,所以传 false
  • 第三个参数指定需要包含的文件的匹配规则,我们用一个正则表示

然后使用 context.keys() 就能拿到该上下文的文件路径列表,而 context 本身也是一个方法,相当于设置过上下文的 require,我们将 require 后的文件放入数组中,数组中的路径其实是带 hash 值的,如下是我项目中的图片:

["/static/img/frame_0.965ef86f.png", "/static/img/frame_1.c7465967.png", "/static/img/frame_2.41e82904.png", "/static/img/frame_3.faef7de9.png", "/static/img/frame_4.27ebbe45.png", "/static/img/frame_5.d98cbebe.png", "/static/img/frame_6.c10859bc.png", "/static/img/frame_7.5e9cbdf0.png", "/static/img/frame_8.b3b92c71.png", "/static/img/frame_9.36660295.png"]

而且如果设置过内联图片的话,数组中可能还有图片的 base64 串。

重构一下

方法三已经解决了我们的问题,而且可以批量 require 某个文件夹中的文件。但是 forEach 那块的逻辑明显是重复的,所以我们当然提取出来啦,以后多个组件调用的时候只需要引入即可:

公共模块:

/**
 * 批量加载帧图片
 * @param {Function} context - require.context 创建的函数
 * @returns {Array<string>} 返回的所有图片
 */
function loadFrames (context) {
 const frames = []
 context.keys().forEach(k => {
  frames.push(context(k))
 })
 return frames
}

组件中:

const context = require.context('./assets/images', false, /frame_\d+.png/)
const frames = loadFrames(context)

大功告成!感兴趣的小伙伴可以点击文末链接查看详细文档~

参考链接
require.context
webpack dynamic require

到此这篇关于webpack 动态批量加载文件的实现方法的文章就介绍到这了,更多相关webpack 动态批量加载文件内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
wordpress之js库集合研究介绍
Aug 17 Javascript
jquery 追加tr和删除tr示例代码
Sep 12 Javascript
JS的Document属性和方法小结
Sep 17 Javascript
javascript创建和存储cookie示例
Jan 07 Javascript
AngularJs concepts详解及示例代码
Sep 01 Javascript
html+javascript+bootstrap实现层级多选框全层全选和多选功能
Mar 09 Javascript
jQuery实现简单的滑动导航代码(移动端)
May 22 jQuery
JavaScript中字符串的常用操作方法及特殊字符
Mar 18 Javascript
小程序组件之仿微信通讯录的实现代码
Sep 12 Javascript
微信小程序中的店铺评分组件及vue中用svg实现的评分显示组件
Nov 16 Javascript
JavaScript函数定义方法实例详解
Mar 05 Javascript
VUE实现自身整体组件销毁的示例代码
Jan 13 Javascript
vue-cli3项目升级到vue-cli4 的方法总结
Mar 19 #Javascript
js实现经典贪吃蛇小游戏
Mar 19 #Javascript
javascrpt密码强度校验函数详解
Mar 18 #Javascript
Node.js Domain 模块实例详解
Mar 18 #Javascript
js判断密码强度的方法
Mar 18 #Javascript
vue项目配置使用flow类型检查的步骤
Mar 18 #Javascript
Vue项目中使用flow做类型检测的方法
Mar 18 #Javascript
You might like
中国的第一台收音机
2021/03/01 无线电
php根据年月获取季度的方法
2014/03/31 PHP
推荐10个提供免费PHP脚本下载的网站
2014/12/31 PHP
PHP格式化MYSQL返回float类型的方法
2016/03/30 PHP
Yii列表定义与使用分页方法小结(3种方法)
2016/07/15 PHP
PHP获取当前系统时间的方法小结
2018/10/03 PHP
Thinkphp极验滑动验证码实现步骤解析
2020/11/24 PHP
jQueryPad 实用的jQuery测试工具(支持IE,chrome,FF)
2010/05/22 Javascript
jQuery EasyUI API 中文文档 - EasyLoader 加载器
2011/09/29 Javascript
javascript限制文本框只允许输入数字(曾经与现在的方法对比)
2013/01/18 Javascript
深入理解JavaScript是如何实现继承的
2013/12/12 Javascript
JavaScript打印网页指定区域的例子
2014/05/03 Javascript
一个非常好用的文字滚动的案例,鼠标悬浮可暂停[两种方案任选]
2016/12/01 Javascript
利用angularjs1.4制作的简易滑动门效果
2017/02/28 Javascript
前端框架学习总结之Angular、React与Vue的比较详解
2017/03/14 Javascript
angular.js4使用 RxJS 处理多个 Http 请求
2017/09/23 Javascript
AngularJs ng-change事件/指令的用法小结
2017/11/01 Javascript
jquery实现二级导航下拉菜单效果实例
2019/05/14 jQuery
微信小程序图片左右摆动效果详解
2019/07/13 Javascript
微信小程序实现音频文件播放进度的实例代码
2020/03/02 Javascript
基于javascript的无缝滚动动画实现2
2020/08/07 Javascript
[01:00:30]TFT vs VGJ.T Supermajor 败者组 BO3 第一场 6.5
2018/06/06 DOTA
在Python的web框架中编写创建日志的程序的教程
2015/04/30 Python
python嵌套函数使用外部函数变量的方法(Python2和Python3)
2016/01/31 Python
django admin后台添加导出excel功能示例代码
2019/05/15 Python
关于Numpy数据类型对象(dtype)使用详解
2019/11/27 Python
Python图像处理库PIL的ImageGrab模块介绍详解
2020/02/26 Python
详解Django关于StreamingHttpResponse与FileResponse文件下载的最优方法
2021/01/07 Python
欧铁通票官方在线销售网站:Eurail.com
2017/10/14 全球购物
30年同学聚会感言
2014/01/30 职场文书
应聘文员自荐信范文
2014/03/11 职场文书
2014年外贸业务员工作总结
2014/12/11 职场文书
服装区域经理岗位职责
2015/04/10 职场文书
小学大队干部竞选稿
2015/11/20 职场文书
Golang连接并操作MySQL
2022/04/14 MySQL
MySQL控制流函数(-if ,elseif,else,case...when)
2022/07/07 MySQL