loader
webpack loader: loader本質(zhì)上是一個(gè)函數(shù),它的作用是將某個(gè)源碼字符串轉(zhuǎn)換成另一個(gè)源碼字符串返回。
loader函數(shù)將在模塊解析的過(guò)程中被調(diào)用,以得到最終的源碼。按照l(shuí)oader配置的書(shū)寫(xiě)順序, 多個(gè)loader從后往前依次調(diào)用, 后面一個(gè)loader的輸出結(jié)果傳入前一個(gè)loader繼續(xù)處理.

代碼示例
var loaderUtils = require('loader-utils');
module.exports = function (sourceCode) {
var options = loaderUtils.getOptions(this);
var reg = new RegExp(options.changeVar, "g");
return sourceCode.replace(reg, "var");
}
在配置文件中的options處書(shū)寫(xiě)的額外參數(shù), 可以在loader函數(shù)內(nèi)的this中讀取到, webpack在this對(duì)象內(nèi)放了很多東西. 使用load-utils包可以幫助我們解析this對(duì)象的內(nèi)容.
plugin
loader的功能定位是轉(zhuǎn)換代碼,而一些其他的操作難以使用loader完成,比如:
- 當(dāng)webpack生成文件時(shí),順便多生成一個(gè)說(shuō)明描述文件
- 當(dāng)webpack編譯啟動(dòng)時(shí),控制臺(tái)輸出一句話表示webpack啟動(dòng)了
- 當(dāng)xxxx時(shí),xxxx
這種類(lèi)似的功能需要把功能嵌入到webpack的編譯流程中,而這種事情的實(shí)現(xiàn)是依托于plugin的. webpack在編譯的過(guò)程中有許多事件, 我們通過(guò)監(jiān)聽(tīng)這些事件能夠參與到編譯過(guò)程的各個(gè)階段, 并且有機(jī)會(huì)在這些階段來(lái)實(shí)現(xiàn)我們的功能.
plugin的本質(zhì)是一個(gè)帶有apply方法的對(duì)象, 習(xí)慣上我們會(huì)將該對(duì)象寫(xiě)成構(gòu)造函數(shù)的模式.
apply方法有一個(gè)參數(shù)compiler, compiler對(duì)象是在初始化階段創(chuàng)建的, 整個(gè)webpack打包過(guò)程中只有一個(gè)compiler對(duì)象, 后續(xù)完成打包工作的是compiler對(duì)象內(nèi)部創(chuàng)建的compilation(每一次具體的打包過(guò)程都會(huì)有新的compilation對(duì)象). apply方法會(huì)在創(chuàng)建好compiler對(duì)象后調(diào)用, 并向方法傳入compiler.
代碼示例
module.exports = class MyPlugin {
constructor(filename = 'filelist.txt') {
this.filename = filename;
}
apply(compiler) {
// 在這里注冊(cè)事件, 類(lèi)似于window.onload
compiler.hooks.emit.tap("MyPlugin-emit", function(compilation) {
// 事件處理函數(shù)
var fileList = [];
for (const key in compilation.assets) {
var content = `[${key}] 大小: ${compilation.assets[key].size()}b`;
fileList.push(content);
}
var str = fileList.join("\n\n");
compilation.assets[this.filename] = {
source() {
return str;
},
size() {
return str.length;
}
};
});
}
}
常用擴(kuò)展
- css-loader: 將css代碼轉(zhuǎn)換為js代碼, 將css中的其他依賴作為require導(dǎo)入, 以便webpack分析依賴
- style-loader: 將css-loader轉(zhuǎn)換后的代碼進(jìn)一步處理, 將樣式加到頁(yè)面的style元素中
- MiniCssExtractPlugin: 將css文件單獨(dú)抽離到輸出目錄(跟js打包方式一樣, 一個(gè)chunk合并成一個(gè)css)
- 它包含有一個(gè)plugin和一個(gè)loader
- 在loader配置處以
MiniCssExtractPlugin.loader的方式, 替換到原先的style-loader
- clean-webpack-plugin: 清除輸出目錄
- html-webpack-plugin: 自動(dòng)生成頁(yè)面
- copy-webpack-plugin: 復(fù)制靜態(tài)資源(html中直接引用靜態(tài)資源, 希望把靜態(tài)資源復(fù)制到輸出目錄)
- webpack-dev-server: 開(kāi)發(fā)服務(wù)器(既不是plugin, 也不是loader), 運(yùn)行webpack-dev-server后, 做了以下操作
- 內(nèi)部執(zhí)行webpack命令, 傳遞命令參數(shù)
- 開(kāi)啟watch
- 注冊(cè)hooks, 向webpack中注冊(cè)鉤子函數(shù), 主要功能有: 將資源列表保存, 禁止webpack輸出文件
- 用express開(kāi)啟一個(gè)服務(wù)器, 監(jiān)聽(tīng)某端口
- file-loader: 將導(dǎo)入的文件, 導(dǎo)出為一個(gè)有效url地址, 并將文件輸出到輸出目錄
- url-loader: 將依賴的文件轉(zhuǎn)換為: 導(dǎo)出一個(gè)base64格式的字符串, 不生成文件到輸出目錄
- url-loader可以配置如果文件超過(guò)了設(shè)定的大小, 則不生成base64編碼, 而是交給file-loader處理
- url-loader內(nèi)部使用了file-loader, 它的配置項(xiàng)有些自身不用, 是傳遞給file-loader用的
webpack內(nèi)置插件
所有的webpack內(nèi)置插件都作為webpack的靜態(tài)屬性存在
const webpack = require("webpack");
new webpack.插件名(options);
- DefinePlugin: 定義一些全局的常量
- BannerPlugin: 為每個(gè)chunk生成的文件頭部添加注釋, 一般用于添加作者, 公司, 版權(quán)信息等
- ProvidePlugin: 自動(dòng)加載模塊, 而不必導(dǎo)出import或require
解決路徑問(wèn)題
在某些情況下, 例如將圖片用file-loader輸出到dist/img目錄下, js文件輸出到dist/scripts目錄下, 頁(yè)面文件輸出到dist/html目錄下, 此時(shí)js對(duì)圖片的引用會(huì)產(chǎn)生問(wèn)題.
這種問(wèn)題發(fā)生的根本原因是: 模塊中的路徑來(lái)自于某個(gè)loader或plugin, 當(dāng)產(chǎn)生路徑時(shí), loader或plugin只有相對(duì)于dist目錄的路徑, 并不知道該路徑將在哪個(gè)資源中使用, 從而無(wú)法確定最終的正確路徑.
面對(duì)這種情況, 需要依靠webpack的配置publicPath解決.
注意: publicPath通常在output配置項(xiàng)中, 本身沒(méi)有任何作用, 但是某些loader和plugin會(huì)讀取這個(gè)publicPath, 并且它們導(dǎo)出的地址前面, 拼接上這個(gè)配置. 通常output里配置為publicPath: "/" 這樣, 文件的地址就變成了絕對(duì)路徑.
一般, 會(huì)用到publicPath的loader和plugin, 本身也會(huì)支持在自己的配置項(xiàng)中再單獨(dú)配置publicPath項(xiàng).