官⽅⽂档: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 方法获取```javascriptconst 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 代替returnthis.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 进行文件写入```javascriptconst 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.jsconsole.log("hello kkk");
//replaceLoader.jsmodule.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.jsmodule: {rules: [{test: /\.js$/,use: [{loader: path.resolve(__dirname, './loader/replaceLoader.js'),options: {name: '哈哈哈'}}]}];}// replaceLoader.jsmodule.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⾥⾯有异步的事情要怎么处理呢```javascriptconst 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目录}
 
