webpack 動(dòng)態(tài)批量加載文件的實(shí)現(xiàn)方法
背景
最近筆者在工作中遇到了一個(gè)小需求:
要實(shí)現(xiàn)一個(gè)組件來播放幀圖片
這個(gè)需求本身不復(fù)雜,但是需要在組件中一次性引入十張圖片,就像下面這樣:
// 就是這么任性,下標(biāo)從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'
作為一個(gè)有代碼潔癖的程序員,我是不允許這種重復(fù)性代碼存在滴,于是乎就嘗試有沒有什么簡單的方法。
方法一:繞過 webpack
由于筆者用的是 vue-cli 3,熟悉的小伙伴都知道,將圖片以固定的格式放在 public 文件夾下面,然后在代碼中直接以絕對(duì)路徑引入即可。這么做的話,就可以根據(jù)文件名構(gòu)造一個(gè) url 數(shù)組,簡單代碼如下:
const frames = [] _.times(10, v => { frames.push(`/images/frame_${v}.png`) }) // 然后你就得到 10個(gè) url 的數(shù)組啦
此方法本身是 vue-cli 提供的一個(gè) 應(yīng)急手段,它有幾個(gè)缺點(diǎn):
- 無法利用 webpack 處理資源,無法產(chǎn)生內(nèi)容哈希,不利于緩存更新
- 無法利用 url-loader 將資源內(nèi)聯(lián)成 base64 字符串 以減少網(wǎng)絡(luò)請(qǐng)求
方法二:require
由于 import 是靜態(tài)關(guān)鍵字,所以如果想要批量加載文件,可以使用 require,但是直接像下面這樣寫是不行的:
const frames = [] _.times(10, v => { const path = `./assets/images/frame_${v}.png` frames.push(require(path)) }
上面的代碼中的 path 是在程序運(yùn)行時(shí)才能確定的,即屬于 runtime 階段,而 webpack 中的 require 是在構(gòu)建階段確定文件位置的,所以 webpack 沒法推測(cè)出這個(gè) path 在哪里。
但是卻可以這樣寫:
const frames = [] _.times(10, v => { frames.push(require(`./assets/images/frame_${v}.png`)) } // frames 中就得到 帶 hash 值的路徑
雖然這兩種寫法在語法上沒有差別,但是第二種寫法在構(gòu)建時(shí)提示了 webpack,webpack 會(huì)將 ./assets/images 中的所有文件都加入到 bundle 中,從而在你運(yùn)行時(shí)可以找到對(duì)應(yīng)的文件。
在使用方法二的時(shí)候筆者嘗試將批量加載的邏輯提取到其他模塊用來復(fù)用:
export function loadAll (n, prefix, suffix) { const frames = [] _.times(n, v => { frames.push(require('./' + prefix + v + suffix)) }) return frames }
但是顯然失敗了,因?yàn)樘崛『蟮拇a,運(yùn)行的 context 屬于另一個(gè)模塊,所以也就無法找到相對(duì)路徑中的文件。
方法三:require.context
上面兩種方法都不算很優(yōu)雅,于是就去翻 webpack 的文檔,終于,讓我找到了這么一個(gè)方法:require.context
require.context( directory: String, includeSubdirs: Boolean /* 可選的,默認(rèn)值是 true */, filter: RegExp /* 可選的,默認(rèn)值是 /^\.\/.*$/,所有文件 */, mode: String /* 可選的,'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默認(rèn)值是 'sync' */ )
指定一系列完整的依賴關(guān)系,通過一個(gè) directory 路徑、一個(gè) includeSubdirs 選項(xiàng)、一個(gè) filter 更細(xì)粒度的控制模塊引入和一個(gè) mode 定義加載方式。然后可以很容易地解析模塊.
我們還是看上面的例子:
const frames = [] const context = require.context('./assets/images', false, /frame_\d+.png/) context.keys().forEach(k => { frames.push(context(k)) })
這里的代碼通過 require.context 創(chuàng)建了一個(gè) require 上下文。
- 第一個(gè)參數(shù)指定了需要加載的文件夾,即組件當(dāng)前目錄下的 ./assets/images 文件夾
- 第二個(gè)參數(shù)指定是否需要包含子目錄,由于沒有子目錄,所以傳 false
- 第三個(gè)參數(shù)指定需要包含的文件的匹配規(guī)則,我們用一個(gè)正則表示
然后使用 context.keys() 就能拿到該上下文的文件路徑列表,而 context 本身也是一個(gè)方法,相當(dāng)于設(shè)置過上下文的 require,我們將 require 后的文件放入數(shù)組中,數(shù)組中的路徑其實(shí)是帶 hash 值的,如下是我項(xiàng)目中的圖片:
["/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"]
而且如果設(shè)置過內(nèi)聯(lián)圖片的話,數(shù)組中可能還有圖片的 base64 串。
重構(gòu)一下
方法三已經(jīng)解決了我們的問題,而且可以批量 require 某個(gè)文件夾中的文件。但是 forEach 那塊的邏輯明顯是重復(fù)的,所以我們當(dāng)然提取出來啦,以后多個(gè)組件調(diào)用的時(shí)候只需要引入即可:
公共模塊:
/** * 批量加載幀圖片 * @param {Function} context - require.context 創(chuàng)建的函數(shù) * @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)
大功告成!感興趣的小伙伴可以點(diǎn)擊文末鏈接查看詳細(xì)文檔~
參考鏈接
require.context
webpack dynamic require
到此這篇關(guān)于webpack 動(dòng)態(tài)批量加載文件的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)webpack 動(dòng)態(tài)批量加載文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
uniapp手寫滾動(dòng)選擇器的完整代碼(時(shí)間選擇器)
這篇文章主要介紹了uniapp手寫滾動(dòng)選擇器的完整代碼(時(shí)間選擇器),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-07-07javascript中的nextSibling使用陷(da)阱(keng)
關(guān)于HTML/XML節(jié)點(diǎn)的問題,在IE中nextSibling不會(huì)返回文本節(jié)點(diǎn),而chrome或者firefox等會(huì)返回文本節(jié)點(diǎn)2014-05-05javascript勻速運(yùn)動(dòng)實(shí)現(xiàn)方法分析
這篇文章主要介紹了javascript勻速運(yùn)動(dòng)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了JavaScript勻速運(yùn)動(dòng)的具體實(shí)現(xiàn)步驟與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-01-01Javascript 拖拽的一些高級(jí)的應(yīng)用(逐行分析代碼,讓你輕松了拖拽的原理)
這篇文章主要介紹了Javascript 拖拽的一些高級(jí)的應(yīng)用(逐行分析代碼,讓你輕松了拖拽的原理),需要的朋友可以參考下2015-01-01es7學(xué)習(xí)教程之Decorators(修飾器)詳解
這篇文章主要給大家介紹了關(guān)于es7中Decorators(修飾器)的相關(guān)資料,文中通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-07-07