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 相关文章推荐
改进:论坛UBB代码自动插入方式
Dec 22 Javascript
javascript静态页面传值的三种方法分享
Nov 12 Javascript
javascript几个易错点记录
Nov 26 Javascript
Node.js中调用mysql存储过程示例
Dec 20 Javascript
JavaScript实现刷新不重记的倒计时
Aug 10 Javascript
bootstrap滚动监控器使用方法解析
Jan 13 Javascript
Vue.js 2.0 移动端拍照压缩图片预览及上传实例
Apr 27 Javascript
在Vue.js中使用Mixins的方法
Sep 12 Javascript
vue-router中的hash和history两种模式的区别
Jul 17 Javascript
Angular6 Filter实现页面搜索的示例代码
Dec 02 Javascript
vue指令做滚动加载和监听等
May 26 Javascript
Vue实现商品详情页的评价列表功能
Sep 04 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
PHP中使用substr()截取字符串出现中文乱码问题该怎么办
2015/10/21 PHP
zend framework中使用memcache的方法
2016/03/04 PHP
CI框架数据库查询之join用法分析
2016/05/18 PHP
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
2010/03/16 Javascript
JavaScript ECMA-262-3 深入解析.第三章.this
2011/09/28 Javascript
JS判断不同分辨率调用不同的CSS样式文件实现思路及测试代码
2013/01/23 Javascript
javascript jq 弹出层实例
2013/08/25 Javascript
页面载入结束自动调用js函数示例
2013/09/23 Javascript
鼠标移到图片上变大显示而不是放大镜效果
2014/06/15 Javascript
基于jQuery+Cookie实现的防止刷新的在线考试倒计时
2015/06/19 Javascript
JavaScript实现的浮动层框架用法实例分析
2015/10/10 Javascript
js采用concat和sort将N个数组拼接起来的方法
2016/01/21 Javascript
JS扩展类,克隆对象与混合类实例分析
2016/11/26 Javascript
js简单正则验证汉字英文及下划线的方法
2016/11/28 Javascript
React中使用collections时key的重要性详解
2017/08/07 Javascript
详解React Native网络请求fetch简单封装
2017/08/10 Javascript
微信小程序使用form表单获取输入框数据的实例代码
2018/05/17 Javascript
vue3.0中的双向数据绑定方法及优缺点
2019/08/01 Javascript
layui动态表头的实现代码
2019/08/22 Javascript
使用webpack搭建vue环境的教程详解
2019/12/31 Javascript
原生js实现购物车功能
2020/09/23 Javascript
[01:00:12]2018DOTA2亚洲邀请赛 4.7 淘汰赛 VP vs LGD 第一场
2018/04/09 DOTA
[04:59]2018DOTA2亚洲邀请赛 4.7 Mineski夺冠时刻
2018/04/09 DOTA
python网络编程之UDP通信实例(含服务器端、客户端、UDP广播例子)
2014/04/25 Python
Windows下安装python MySQLdb遇到的问题及解决方法
2017/03/16 Python
Python网络爬虫神器PyQuery的基本使用教程
2018/02/03 Python
五分钟带你搞懂python 迭代器与生成器
2020/08/30 Python
用python读取xlsx文件
2020/12/17 Python
CSS3 background-image颜色渐变的实现代码
2018/09/13 HTML / CSS
使用HTML5 Canvas为图片填充颜色和纹理的教程
2016/03/21 HTML / CSS
英国浴室洗脸盆购物网站:Click Basin
2018/06/08 全球购物
Feelunique澳大利亚:欧洲的化妆品零售电商
2019/12/18 全球购物
俄罗斯运动、健康和美容产品在线商店:Lactomin.ru
2020/07/23 全球购物
保险专业大专生求职信
2013/10/26 职场文书
十岁生日家长答谢词
2014/01/17 职场文书
禁止高声喧哗的标语
2014/06/11 职场文书