自定义 Loader
Loader 本质上是一个导出为函数的 JavaScript 模块,webpack 内部会调用这个函数,然后将上一个 loader 产生的结果传递进去。
这个导出的函数中需要用到 this,所以说导出的不能是一个箭头函数。
// loader/customer-loader.jsmodule.exports = function(content) {// ...}
加载 Loader 的路径
一般在使用一个 loader 时方式如下:
{
use: [
// 直接指定 loader 的名称
'style-loader',
'css-loader'
],
}
使用上面这种第三方 loader 时,直接指定的就是 loader 的名称,webpack 会去 node_modules 中查找这个 loader,而我们自定义的 loader 就不能直接这样写了,解决这个问题的方法有两种。
方法一:配置相对路径
可以直接配置一个相对路径,但是这个相对路径是基于 webpack.context 这个属性的,所以说一定要确认好 content 的路径。
// webpack.config.js
module.exports = {
context: __dirname,
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: './loaders/customer-loader', // .js 后缀可加可不加
},
],
},
],
},
}
方法二:配置 resolveLoader 属性:
第三方 loader 可以去 node_module 中查找就是因为 resolveLoader.module 这个配置,这个配置的默认值就是 node_modules,我们也可以配置其他路径。
// webpack.config.js
module.exports = {
resolveLoader: {
modules: ['node_modules', './loaders'],
},
}
Loader 的执行循序
在自定义 loader 的模块中,还可以导出一个 pitch 的函数,也成为 pitch-loader(默认导出的为 normal- loader),如下:
// loader/customer-loader.js
module.exports = function(content) {
// ...
}
// 在上面的函数中挂载了一个 pitch 函数
module.exports.pitch = function() {
// ...
}
在执行 loader 时会优先执行顺序 pitchLoader,然后再倒序指定 normalLoader,一般只会用到 normalLoader,所以这也是为什么说 loader 是从后往前执行的原因。
enforce
enforce 可以控制 loader 的执行顺序,一共有两个值:
- pre
- post
默认所有 loader 为 normal,如果是在行内指定的 loader 为 inline。
PitchLoader 的执行顺序是:
post, inline, normal, pre。
NormalLoader 的执行顺序是:
pre, normal, inline, post。
如果要控制一个 loader 的执行顺序需要写多个 rule:
module.exports = {
module: {
rules: [
{
test: /\.js$/i,
use: 'babel-loader',
},
{
test: /\.js$/i,
use: 'customer-loader',
enforce: 'pre', // 优先执行
},
],
},
}
异步 Loader
自定义 Loader 导出的函数默认是同步的,且必须把 content 再返回,但是如果在这个函数中出现异步情况时,这时需要先调用 `this.async` 获取一个 callback,这个 callback 第一个参数为错误对象,没有则传递 null,第二个是处理的结果,使用示例如下:
module.exports = function(content) {
const callback = this.async();
setTimeout(() => {
callback(null, content);
}, 2000);
}
获取传递配置
在使用 loader 时可能会传递一些配置项,如果想要获取这些配置项,我们可以借助一个库 loader-utils。
安装依赖:
npm i loader-utils -D
获取配置:
const { getOptions } = require('loader-utils');
module.exports = function(context) {
const options = getOptions(this);
// options 就是传递的配置了
}
配置参数校验
schema-utils 这个库可以帮助我们完成传递的配置参数校验,查看是否正确的传递了对应的配置。
安装依赖:
npm i schema-utils -D
创建 schema,schema 是一个 json 文件:
{
"type": "object",
"properties": {
"preset": {
"type": "array",
"description": "请传递 preset"
},
"test": {
"type": "boolean",
"description": "请传递 test"
}
}
}
修改 loader:
const { validate } = require('schema-utils');
const { getOptions } = require('loader-utils');
const schema = require('../schema/babel-loader-schema.json');
module.exports = function(content) {
const options = getOptions(this);
// 校验
validate(schema, options);
return content;
}
如果传递的 test 属性不是一个 boolean 时,就会报错了:<br />
自定义一个简单的 Babel-loader
首先要安装 babel 依赖:
npm i @babel/core @babel/preset-env
创建 babel-loader:
// loaders/babel-loader.js
const babel = require('@babel/core');
const { getOptions } = require('loader-utils');
module.exports = function(content) {
const callback = this.async();
const options = getOptions(this);
babel.transform(content, options, (err, result) => {
if (err) {
callback(err);
} else {
callback(null, result.code);
}
});
}
使用:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env'],
],
},
},
],
},
],
},
resolveLoader: ['node_modules', './loaders'],
}
