如何编写一个loader
loader其实就是个函数,但是不能使用箭头函数
module.exports = function (source) {return source.replace("LC", "supercll");};
module: {rules: [{test: /\.js/,use: [path.resolve(__dirname, "./loaders/replaceLoader.js")],},],},

这样其实就完成了一个最简单的loader
配置选项
loader函数中的this.query接受了从use对象中传递的options参数
module.exports = function (source) {console.log(this.query);return source.replace("LC", this.query.name);};
module: {rules: [{test: /\.js/,use: [{loader: path.resolve(__dirname, "./loaders/replaceLoader.js"),options: {name: "lc",},},],},],},
解析参数
有时候config传入的参数比较复杂,这个时候就可以使用loader-utils模块来帮助我们解析这些参数
const loaderUtils = require("loader-utils");
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
return source.replace("LC", options.name);
};
常用方法
this.callback
可以额外把源代码,sourceMap或者其他额外的信息返回出去
基本配置
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
const loaderUtils = require("loader-utils");
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const res = source.replace("LC", options.name);
this.callback(null, res);
};
this.async编写异步loader
如果loader函数中有异步代码时,
const loaderUtils = require("loader-utils");
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const callback = this.async();
let timer = setTimeout(() => {
clearTimeout(timer);
const res = source.replace("LC", options.name);
callback(null, res);
}, 2000);
};
多loader打包
module: {
rules: [
{
test: /\.js/,
use: [
{ loader: path.resolve(__dirname, "./loaders/replaceLoader.js") },
{
loader: path.resolve(__dirname, "./loaders/replaceLoaderAsync.js"),
options: {
name: "lc",
},
},
],
},
],
},
resolveLoader配置自动寻找路径
resolveLoader: {
modules: ["node_modules", "./loaders"],
},
module: {
rules: [
{
test: /\.js/,
use: [
{ loader: "replaceLoader" },
{
loader: "replaceLoaderAsync",
options: {
name: "lc",
},
},
],
},
],
},
这样就便于引入自定义loader
实际应用
如果使用一个库的时候,想给这个库添加格外的功能或者异常检测的代码,就可以写一个loader,通过抽象语法树分析所有的代码
- 比如遇到一个function,就可以给这个function添加trycatch,即时捕获异常
- 再比如我想要页面有多种语言,这个时候就可以编写loader,把文字放入到占位符中,通过判断全局变量,把所有的文字进行一个全局替换
- 非常方便批量的一个修改
如何编写一个plugin
plugin可以处理模块,
是一个钩子函数,在指定的周期阶段生效
plugin不同于loader,他是一个类
类中有个apply方法,有一个参数compiler,里面传入的是webpack 的实例
基本格式
class CopyRightWebpackPlugin {
constructor() {}
apply(compiler) {}
}
module.exports = CopyRightWebpackPlugin;
compiler里包含了hooks
hooks属性里包含了webpack各种周期钩子函数
compiler钩子
emit
AsyncSeriesHook
生成资源到 output 目录之前。
参数:compilation
这是个异步钩子,需要异步包装一下
tapAsync包装为异步,第一个参数是plugin类的名字
class CopyRightWebpackPlugin {
apply(compiler) {
// hooks属性里包含了webpack各种周期函数
compiler.hooks.emit.tapAsync("CopyRightWebpackPlugin", (compilation, cb) => {
// compilation存放了这次的配置内容,cb
console.log(123);
cb();
});
}
}
module.exports = CopyRightWebpackPlugin;
钩子函数的参数
- compilation,不同于compiler,complication存放了这次打包的所有配置
- assets
- cb回调函数
class CopyrightWebpackPlugin {
apply(compiler) {
// hooks属性里包含了webpack各种周期函数
compiler.hooks.emit.tapAsync("CopyRightWebpackPlugin", (compilation, cb) => {
// compilation存放了这次的配置内容,cb
// console.log(compilation.assets)
compilation.assets["copyright.txt"] = {
source: function () {
return "copyright by all lc";
},
size: function () {
// 文件大小
return 20;
},
};
cb();
});
}
}
module.exports = CopyrightWebpackPlugin;
compile
这是一个同步的周期钩子SyncHook
一个新的编译(compilation)创建之后,钩入(hook into) compiler。
参数:compilationParams
同步就使用tap函数包装,并且只需要传入一个参数complication
class CopyrightWebpackPlugin {
apply(compiler) {
// hooks属性里包含了webpack各种周期函数
compiler.hooks.compile.tap("CopyrightWebpackPlugin", complication => {
console.log("compile");
});
}
}
如何快速查看plugin属性
- 开启node调试工具—inspect
- —inspect-brk,自动在第一行打一个断点
运行脚本debug,打开webpack官网{ "name": "plugin", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "debug": "node --inspect --inspect-brk node_modules/webpack/bin/webpack.js", "build": "webpack" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^5.1.3", "webpack-cli": "^4.1.0" } }


这样就可以得到各种的属性
总结
- 编写loader
- loader其实是一个函数,创建一个loader文件通过module。exports的方式导出,但是不能使用箭头函数
- webpack传入loader函数的所有信息都保存在this中
- this.query存放了use中的options属性
- 可以使用loader-utils来帮助解析这些属性
- 当我希望loader函数返回异常,源码,sourceMap或者其他额外的信息的时候可以使用this.callback
- 还有编写异步loader的时候不是原生支持的,可以使用this.async来包装为异步函数
- loader实际应用在于
- 比如我想给源码中的function 函数都添加上一个异常捕获的功能,tyrcatch
- 或者说我的页面希望有多种语言展示,那么就可以编写loader实现一个全局替换
- 非常便于一个批量修改把,达到一个自动化的目的
- 编写plugin
- 与loader不同,plugin是一个类,平常使用的时候就能体现出来
- plugin类中有一个apply方法,方法中有一个参数compiler,compiler中存放的是webpack的一个实例
- 而compiler中包含了hooks这个属性,hooks中又存放了各种webpack的周期钩子函数,可以调用这些钩子函数来达到在webpack各个周期增加功能的功能
- 需要注意的是webpack的周期钩子有两种状态,一个是同步的一个是异步的
- 在使用同步钩子时需要在后边加上tap方法来包装钩子函数,tap的第一个参数是这个插件的名字,第二个参数就是回调函数了
- 如果是异步钩子的话,则需要使用tabAsync来包装,tabAsync第二个参数回调函数多了一个参数callback,使用的时候需要在最后调用一次这个cb函数达到异步回调的目的
