官⽅⽂档:https://webpack.js.org/contribute/writing-a-loader/
接⼝⽂档:https://webpack.js.org/api/loaders/
⾃⼰编写⼀个 Loader 的过程是⽐较简单的,Loader 就是⼀个函数,声明式函数,不能⽤箭头函数拿到源代码,作进⼀步的修饰处理,再返回处理后的源码就可以了
Webpack 加载资源文件的过程类似于一个工作管道,你可以在这个过程中依次使用多个 Loader,但是最终这个管道结束过后的结果必须是一段标准的 JS 代码字符串
一个最简单的 loader
定义:loader 只是一个导出为函数的 JavaScript 模块
module.exports = function(source) {
return source;
};
多个 Loader 串行执行:顺序从后到前
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /\.less$/,
use: ['style-loader','css-loader',' less-loader']
}]
}
};
loader-runner
定义:loader-runner 允许你在不安装 webpack 的情况下运行 loaders
作用:
- 作为 webpack 的依赖,webpack 中使用它执行 loader
- 进行 loader 的开发和调试
```javascript const { runLoaders } = require(‘loader-runner’); const fs = require(‘fs’); const path = require(‘path’);mport { runLoaders } from "loader-runner";
runLoaders({
resource: “/abs/path/to/file.txt?query”, // String: 资源的绝对路径(可以增加查询字符串)
loaders: [“/abs/path/to/loader.js?query”], // String[]: loader 的绝对路径(可以增加查询字符串)
context: { minimize: true }, // 基础上下文之外的额外 loader 上下文
readResource: fs.readFile.bind(fs) // 读取资源的函数
}, function(err, result) {
// err: Error?
// result.result: Buffer | String
})
runLoaders({ resource: path.join(dirname, ‘./src/demo.txt’), loaders: [ { loader: path.join(dirname, ‘./src/raw-loader.js’), options: { name: ‘test’ } } ], context: { emitFile: () => {} }, readResource: fs.readFile.bind(fs) }, (err, result) => { err ? console.log(err) : console.log(result); });

<a name="E4uFT"></a>
## loader 的参数获取
通过 loader-utils 的 getOptions 方法获取
```javascript
const loaderUtils = require("loader-utils");
module.exports = function(content) {
const { name } = loaderUtils.getOptions(this); //获取options参数
};
this.callback
loader 内直接通过 throw 抛出,或通过 this.callback 传递错误
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
this.callback(new Error('Error'), ''); //传递错误
//也可以直接 throw new Error('Error');
// 可通过callback 代替return
this.callback(null, data);
// 如何返回多个信息,不⽌是处理好的源码,可以使⽤this.callback来处理
this.callback(null, data, 'test');
异步处理
通过 this.async
来返回一个异步函数
- 第一个参数是 Error,第二个参数是处理的结果
```javascript const callback = this.async();module.exports = function(input) {
const callback = this.async();
// No callback -> return synchronous results
// if (callback) { ... }
callback(null, input + input);
};
fs.readFile(path.join(__dirname, ‘./async.txt’), ‘utf-8’, (err, data) => { if (err) { callback(err, ‘’); } callback(null, data); });
<a name="b6SqT"></a>
##
<a name="GVWSG"></a>
## 在 loader 中使用缓存
webpack 中默认开启 loader 缓存
- 可以使用 this.cacheable(false) 关掉缓存
缓存条件: loader 的结果在相同的输入下有确定的输出
- 有依赖的 loader 无法使用缓存
<a name="lFj5c"></a>
## loader 进行文件输出
通过 this.emitFile 进行文件写入
```javascript
const loaderUtils = require("loader-utils");
module.exports = function(content) {
const url = loaderUtils.interpolateName(this, "[hash].[ext]", {
content,
});
this.emitFile(url, content);
const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;
return `export default ${path}`;
};
创建一个替换源码中字符串的loader
//index.js
console.log("hello kkk");
//replaceLoader.js
module.exports = function (source) {
console.log(source, this, this.query);
return source.replace('kkk', '哈哈哈');
};
//需要⽤声明式函数,因为要用到上下⽂的this,⽤到this的数据,该函数接受⼀个参数,是源码
- 在配置⽂件中使⽤loader
```javascript
//需要使⽤node核⼼模块path来处理路径
const path = require(‘path’);
module: {
rules: [
{
} ]; }test: /\.js$/,
use: path.resolve(__dirname, './loader/replaceLoader.js')
- **配置参数**
```javascript
//webpack.config.js
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve(__dirname, './loader/replaceLoader.js'),
options: {
name: '哈哈哈'
}
}
]
}
];
}
// replaceLoader.js
module.exports = function (source) {
//this.query 通过this.query来接受配置⽂件传递进来的参数
return source.replace('kkk', this.query.name);
};
- 如何返回多个信息,不⽌是处理好的源码呢,可以使⽤this.callback来处理
- 官⽅推荐处理loader,query的⼯具 ```javascript //replaceLoader.js const loaderUtils = require(‘loader-utils’); //官⽅推荐处理loader,query的⼯具 module.exports = function (source) { const options = loaderUtils.getOptions(this); const result = source.replace(‘kkk’, options.name); this.callback(null, result); };
- 如果loader⾥⾯有异步的事情要怎么处理呢
```javascript
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
// 定义⼀个异步处理,告诉webpack,这个loader⾥有异步事件,在⾥⾯调⽤下这个异步
// callback 就是 this.callback 注意参数的使⽤
const callback = this.async();
setTimeout(() => {
const result = source.replace('kkk', options.name);
callback(null, result);
}, 3000);
};
- 处理loader的路径问题
resolveLoader: {
modules: ['node_modules', './loader']; //增加一个loader目录
}