咱們在使用webpack配置項目時會使用過各類各樣的loader和plugin,例如less-loader、file-loader、html-webpack-plugin、clean-webpack-plugin等等。可是咱們是否想過這些loader或plugin是如何編寫的呢?今天咱們就來了解一下如何實現的loader和plugin.也方便咱們之後編寫本身的loader或者pluginjavascript
loader是文件加載器,可以加載資源文件,並對這些文件進行一些處理,諸如編譯、壓縮等,最終一塊兒打包到指定的文件中html
其實loader簡單來講就是一個函數,經過一個函數參數接受到源代碼,並在函數內部對源代碼做出變動,並最終返回源代碼前端
咱們就來實現一個能夠接受txt文件並對其進行首字母大寫的loader,由於這個沒啥用,咱們就單純的用做了解laoder編寫的過程。
第一步:新建項目並配置好webpackjava
npm init
npm install -D webpack webpack-cli
複製代碼
webpack.config.jsnode
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
複製代碼
src文件夾下index.js與index.txtwebpack
// index.js
import './index.txt';
// index.txt
複製代碼
第二步:編寫loader
新建一個loader文件夾存放本身編寫的loader,這裏咱們新建一個uppercaseLoader.js文件。web
module.exports = function (source) {
// 這裏的source表明的是源代碼
}
複製代碼
咱們是要將txt中字符串進行首字符大寫的轉換,接下來直接完善uppercaseLoader.js便可npm
module.exports = function(source) {
return result = source.charAt(0).toUpperCase() + source.slice(1)
}
複製代碼
第三步:使用loader
要使用咱們本身編寫的loader,不是像其餘的loader同樣直接在module裏面配置就好了,還須要配合resolveLoader來使用。這樣配置的意義是在module中只用寫loader名稱,webpack會先到node_modules裏面找,找不到就去當前目錄下的loaders中去找。json
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
resolveLoader: {
modules: ['node_modules', './loaders']
},
module: {
rules: [{
test: /\.txt$/, // 專門處理txt文件
use: [
{
loader: 'uppercaseLoader',
},
]
}]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
複製代碼
package.json添加以下配置: "script: { "build": "webpack"}"api
接下來運行npm run build,會生產一個dist文件,在dist文件下main.js中你會看到已經對txt文件下的字符串進行了首字母大寫轉換。
第四步:loader參數配置
loader一般還可使用參數,可使用loader-utils來讀取loader的參數,例如咱們如今只對首字母爲a的字符串進行大寫轉換,咱們能夠這麼作。
npm install --save-dev loader-utils
複製代碼
改寫webpack.config.js
...
rules: [{
test: /\.txt$/,
use: [
{
loader: 'uppercaseLoader',
options: {
initial: 'a'
}
},
]
}]
複製代碼
uppercaseLoader.js
const loaderUtils = require('loader-utils');
module.exports = function(source) {
const options = loaderUtils.getOptions(this); // 讀取配置
if (options.initial === source.charAt(0)) {
return result = source.charAt(0).toUpperCase() + source.slice(1)
}
return source
}
複製代碼
有時候咱們不止要return一個resource,若是想要返回err, 處理後源代碼,source,或者其餘內容,那麼可使用this.callback.
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any );
module.exports = function (source) {
// ...
this.callback(null, result);
}
複製代碼
若是想要在函數內部作異步處理那麼可使用this.async()
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const callback = this.async(); // 聲明一下內部有異步操做
setTimeout(() => {
let result = source
if (options.initial === source.charAt(0)) {
return result = source.charAt(0).toUpperCase() + source.slice(1)
}
callback(null, result);
}, 1000);
}
複製代碼
編寫loader須要注意的是不要使用箭頭函數,會致使this指向錯誤
plugin是一個類,使用時plugins(插件包)中單獨配置,每一項是一個 plugin 的實例,參數都經過構造函數傳入。在 Webpack 運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在合適的時機經過 Webpack 提供的 API 改變輸出結果,plugin讓 webpack 具備更多的靈活性,提高開發效率.
咱們來實現一個向打包文件中添加一個md說明文檔功能的addMdWebpackPlugin
第一步:新建項目並配置好webpack
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
複製代碼
第二步:編寫plugin
plugin由於是一個類,那麼他的基本結構以下:
// addMdWebpackPlugin.js
class addMdWebpackPlugin {
constructor(options){
console.log(options) // options是插件傳入的參數
}
apply(compiler) { // compiler是webpack的實例}
}
module.exports = addMdWebpackPlugin;
複製代碼
compiler是在 Webpack 啓動時候被實例化的,做爲webpack的實例compiler包含了Webpack 環境全部的的配置信息,包含 options,loaders,plugins 這些信息。
結構寫好以後接下來完善plugin,由於咱們要在打包的文件中添加一個md文檔,那麼咱們就要用到compiler的hooks,由於hooks表明着鉤子函數,是webpack在執行過程當中必經的過程,在hooks裏面又定義了一些具體的時刻值。咱們這裏用到的是emit時刻,官網這樣描述emmit:Executed right before emitting assets to output dir.意思是在打包輸出dist以前執行。而且要注意的是emit是一個異步的時刻值。那麼咱們能夠這樣來寫:
class addMdWebpackPlugin {
constructor(options){
console.log(options) // options是插件傳入的參數
}
apply(compiler) { // compiler是webpack的實例
compiler.hooks.emit.tapAsync('addMdWebpackPlugin', (compilation, cb) => {
compilation.assets['describe.md']= { // compilation.assets是打包生成的文件,能夠向其中添加內容
source: function() {
return 'hello word'
},
size: function() {
return 21;
}
};
cb();
})
}
}
module.exports = addMdWebpackPlugin;
複製代碼
更多關於compiler的知識請看這裏webpack.js.org/api/compile…
第三步:使用plugin
plugin寫好以後,須要webpack中引入並使用插件
const addMdWebpackPlugin = require('./addMdWebpackPlugin');
module.exports = {
...// 省略
plugins: [
new addMdWebpackPlugin()
],
}
複製代碼
執行npm run build,便可在打包完成的dist目錄中看到一個md文檔。這個過程進行了以下步驟:
經過上述的兩個例子,儘管兩個例子沒有什麼大的用處,編寫都也很是簡單,可是我也學習到了應該如何編寫loader和plugin,但願也能夠打開你們編寫loader和plugin的大門。webpack博大精深,若是想要寫出更加有效和實用的loader或plugin,咱們仍是須要不斷的學習。但這也讓咱們在之後遇到須要編寫本身的loader或者plugin的狀況下,不至於無從下手。
仍是那句話,學習中分享,分享中學習,歡迎關注無畏前端!!