字節(jié)面試官:談?wù)刦ile-loader和url-loader的區(qū)別,能不能手寫出來(lái)?
什么是 url-loader
url-loader 會(huì)將引入的文件進(jìn)行編碼,生成 DataURL,相當(dāng)于把文件翻譯成了一串字符串,再把這個(gè)字符串打包到 JavaScript。
使用 base64 來(lái)加載圖片也是有兩面性的:
優(yōu)點(diǎn):節(jié)省請(qǐng)求,提高頁(yè)面性能 缺點(diǎn):增大本地文件大小,降低加載性能
所以我們得有取舍,只對(duì)部分小 size 的圖片進(jìn)行 base64 編碼,其它的大圖片還是發(fā)請(qǐng)求吧。
什么是 file-loader
在css文件中定義background的屬性或者在html中引入image的src,我們知道在webpack打包后這些圖片會(huì)打包至定義好的一個(gè)文件夾下,和開發(fā)時(shí)候的相對(duì)路徑會(huì)不一樣,這就會(huì)導(dǎo)致導(dǎo)入圖片路徑的錯(cuò)誤。而file-loader正是為了解決此類問(wèn)題而產(chǎn)生的,他修改打包后圖片的儲(chǔ)存路徑,再根據(jù)配置修改我們引用的路徑,使之對(duì)應(yīng)引入
之間有什么聯(lián)系呢?
url-loader內(nèi)部封裝了file-loader。url-loader不依賴于file-loader,即使用url-loader時(shí),只需要安裝url-loader即可,不需要安裝file-loader。
通過(guò)上面的介紹,我們可以看到,url-loader工作分兩種情況:
文件大小小于limit參數(shù),url-loader將會(huì)把文件轉(zhuǎn)為DataURL; 文件大小大于limit,url-loader會(huì)調(diào)用file-loader進(jìn)行處理,參數(shù)也會(huì)直接傳給file-loader。因此我們只需要安裝url-loader即可
手寫實(shí)現(xiàn)file-loader
file-loader 的工作流程如下:
通過(guò) loaderUtils.interpolateName 方法可以根據(jù) options.name 以及文件內(nèi)容生成一個(gè)唯一的文件名 url(一般配置都會(huì)帶上hash,否則很可能由于文件重名而沖突)
通過(guò) this.emitFile(url, content) 告訴 webpack 我需要?jiǎng)?chuàng)建一個(gè)文件,webpack會(huì)根據(jù)參數(shù)創(chuàng)建對(duì)應(yīng)的文件,放在 public path 目錄下。
返回 'module.exports = webpack_public_path + '+ JSON.stringify(url) + ‘;’ ,這樣就會(huì)把原來(lái)的文件路徑替換為編譯后的路徑
對(duì)file-loader 中最重要的幾行代碼解釋如下(我們自己來(lái)實(shí)現(xiàn)一個(gè) file-loader 就只需要這幾行代碼就行了,完全可以正常運(yùn)行且支持 name 配置):
var loaderUtils = require('loader-utils')
module.exports = function (content) {
// 獲取options,就是 webpack 中對(duì) file-loader 的配置,比如這里我們配置的是 `name=[name]_[hash].[ext]`
// 獲取到的就是這樣一個(gè)k-v 對(duì)象 { name: "[name]_[hash].[ext]" }
const options = loaderUtils.getOptions(this) || {};
// 這是 loaderUtils 的一個(gè)方法,可以根據(jù) name 配置和 content 內(nèi)容 生成一個(gè)文件名。為什么需要 文件內(nèi)容呢?這是為了保證當(dāng)文件內(nèi)容沒(méi)有發(fā)生變化的時(shí)候,名字中的 [hash] 字段也不會(huì)變??梢岳斫鉃橛梦募膬?nèi)容作了一個(gè)hash
let url = loaderUtils.interpolateName(this, options.name, {
content
})
this.emitFile(url, content) // 告訴webpack,我要?jiǎng)?chuàng)建一個(gè)文件,文件名和內(nèi)容,這樣webpack就會(huì)幫你在 dist 目錄下創(chuàng)建一個(gè)對(duì)應(yīng)的文件
// 這里要用到一個(gè)變量,就是 __webpack_public_path__ ,這是一個(gè)由webpack提供的全局變量,是public的根路徑
// 參見(jiàn):https://webpack.js.org/guides/public-path/#on-the-fly
// 這里要注意一點(diǎn):這個(gè)返回的字符串是一段JS,顯然,他是在瀏覽器中運(yùn)行的
// 舉個(gè)栗子:
// css源碼這樣寫:background-image: url('a.png')
// 編譯后變成: background-image: require('xxxxxx.png')
// 這里的 require 語(yǔ)句返回的結(jié)果,就是下面的 exports 的字符串,也就是圖片的路徑
return 'module.exports = __webpack_public_path__ + '+ JSON.stringify(url)
}
// 一定別忘了這個(gè),因?yàn)槟J(rèn)情況下 webpack 會(huì)把文件內(nèi)容當(dāng)做UTF8字符串處理,而我們的文件是二進(jìn)制的,當(dāng)做UTF8會(huì)導(dǎo)致圖片格式錯(cuò)誤。
// 因此我們需要指定webpack用 raw-loader 來(lái)加載文件的內(nèi)容,而不是當(dāng)做 UTF8 字符串傳給我們
module.exports.raw = true
手寫url-loader
url-loader 的工作流程如下:
獲取 limit 參數(shù) 如果 文件大小在 limit 之類,則直接返回文件的 base64 編碼后內(nèi)容 如果超過(guò)了 limit ,則調(diào)用 file-loader 因?yàn)檫壿嫳容^簡(jiǎn)單,這里直接放上源碼以及我添加的注釋:
module.exports = function(content) {
// 獲取 options 配置,上面已經(jīng)講過(guò)了就不在重復(fù)
var options = loaderUtils.getOptions(this) || {};
// Options `dataUrlLimit` is backward compatibility with first loader versions
// limit 參數(shù),只有文件大小小于這個(gè)數(shù)值的時(shí)候我們才進(jìn)行base64編碼,否則將直接調(diào)用 file-loader
var limit = options.limit || (this.options && this.options.url && this.options.url.dataUrlLimit);
if(limit) {
limit = parseInt(limit, 10);
}
var mimetype = options.mimetype || options.minetype || mime.lookup(this.resourcePath);
// No limits or limit more than content length
if(!limit || content.length < limit) {
if(typeof content === "string") {
content = new Buffer(content);
}
// 直接返回 base64 編碼的內(nèi)容
return "module.exports = " + JSON.stringify("data:" + (mimetype ? mimetype + ";" : "") + "base64," + content.toString("base64"));
}
// 超過(guò)了文件大小限制,那么我們將直接調(diào)用 file-loader 來(lái)加載
var fallback = options.fallback || "file-loader";
var fallbackLoader = require(fallback);
return fallbackLoader.call(this, content);
}
// 一定別忘了這個(gè),因?yàn)槟J(rèn)情況下 webpack 會(huì)把文件內(nèi)容當(dāng)做UTF8字符串處理,而我們的文件是二進(jìn)制的,當(dāng)做UTF8會(huì)導(dǎo)致圖片格式錯(cuò)誤。
// 因此我們需要指定webpack用 raw-loader 來(lái)加載文件的內(nèi)容,而不是當(dāng)做 UTF8 字符串傳給我們
// 參見(jiàn):https://webpack.github.io/docs/loaders.html#raw-loader
module.exports.raw = true
總結(jié)
file-loader 返回的是文件的路徑 url-loader 返回的是文件的base64編碼
參考文獻(xiàn)
詳解webpack url-loader和file-loader:https://segmentfault.com/a/1190000018987483 webpack 源碼解析:file-loader 和 url-loader:https://www.cnblogs.com/shiyunfront/articles/8944940.html
最后
歡迎關(guān)注 《前端陽(yáng)光》公眾號(hào),發(fā)送加群,可加入技術(shù)交流群和各大廠內(nèi)推群,也可以回復(fù)內(nèi)推碼,獲得各大廠內(nèi)推碼。
