# 编写自己的Loader

在我们使用 Webpack 的过程中,我们使用了很多的loader,那么那些loader是哪里来的?我们能不能写自己的loader然后使用? 答案当然是可以的,Webpack 为我们提供了一些loader的API,通过这些API我们能够编写出自己的loader并使用。

# 如何编写及使用自己的Loader

场景

我们需要把.js文件中,所有出现Webpack is good!,改成Webpack is very good!。实际上我们需要编写自己的loader,所以我们有如下的步骤需要处理:

  • 新建webpack-loader项目
  • 使用npm init -y命令生成package.json文件
  • 创建webpack.config.js文件
  • 创建src目录,并在src目录下新建index.js
  • 创建loaders目录,并在loader目录下新建replaceLoader.js
  • 安装webpackwebpack-cli

按上面的步骤新建后的项目目录如下:

|-- loaders
|   | -- replaceLoader.js
|-- src
|   | -- index.js
|-- webpack.config.js
|-- package.json
1
2
3
4
5
6

首先需要在webpack.config.js中添加下面的代码:

const path = require('path');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [path.resolve(__dirname, './loaders/replaceLoader.js')]
      }
    ]
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

随后在package.json文件添加build打包命令:


 



// 其它配置
"scripts": {
  "build": "webpack"
}
1
2
3
4

接下来在src/index.js文件中添加一行代码:这个文件使用最简单的例子,只是打印一句话。

console.log('Webpack is good!');
1

最后就是在loader/replaceLoader.js编写我们自己loader文件中的代码:

说明

  • 编写loader时,module.exports是固定写法,并且它只能是一个普通函数,不能写箭头函数(this指向)
  • source是打包文件的源文件内容
const loaderUtils = require('loader-utils');
module.exports = function(source) {
  return source.replace('good', 'very good');
}
1
2
3
4

使用我们的loader: 要使用我们的loader,则需要在modules中写loader:

理解

resolveLoader:它告诉了 Webpack 使用loader时,应该去哪些目录下去找,默认是node_modules,做了此项配置后,我们就不用去显示的填写其路径了,因为它会自动去loaders文件夹下面去找。













 













const path = require('path');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  resolveLoader: {
    modules: ['node_modules', './loaders']
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [{
          loader: 'replaceLoader',
          options: {
            name: 'wanghuayu'
          }
        }]
      }
    ]
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

最后我们运行npm run build,在生成的dist目录下打开main.js文件,可以看到文件内容已经成功替换了,说明我们的loader已经使用成功了。

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("console.log('Webpack is very good!');\n\n//# sourceURL=webpack:///./src/index.js?");

/***/ })

/******/ });
1
2
3
4
5
6
7
8
9
10
11
12

# 如何向Loader传参及返回多个值

问题

  • 我们如何返回多个值?
  • 我们如何向自己的Loader传递参数?

# 如何返回多个值

说明

Webpack 的 API允许我们使用callback(error, result, sourceMap?, meta?)返回多个值,它有四个参数:

  • Error || Null :错误类型, 没有错误传递null
  • result :转换后的结果
  • sourceMap:可选参数,处理分析后的sourceMap
  • meta: 可选参数,元信息

返回多个值,可能有如下情况:

// 第三,第四个参数是可选的。
this.callback(null, result);
1
2

# 如何传递参数

我们知道在使用loader的时候,可以写成如下的形式:

// options里面可以传递一些参数
{
  test: /\.js$/,
  use: [{
    loader: 'replaceLoader',
    options: {
      word: 'very good'
    }
  }]
}
1
2
3
4
5
6
7
8
9
10

再使用options传递参数后,我们可以使用官方提供的loader-utils来获取options参数,可以像下面这样写:

const loaderUtils = require('loader-utils');
module.exports = function(source) {
  var options = loaderUtils.getOptions(this);
  return source.replace('good', options.word)
}
1
2
3
4
5

# 如何在Loader中写异步代码

在上面的例子中,我们都是使用了同步的代码,那么如果我们有必须异步的场景,该如何实现呢?我们不妨做这样的假设,先写一个setTimeout

const loaderUtils = require('loader-utils');
module.exports = function(source) {
  var options = loaderUtils.getOptions(this);
  setTimeout(() => {
    var result = source.replace('World', options.name);
    return this.callback(null, result);
  }, 0);
}
1
2
3
4
5
6
7
8

如果你运行了npm run build进行打包,那么一定会报错,解决办法是:使用this.async()主动标识有异步代码:




 






const loaderUtils = require('loader-utils');
module.exports = function(source) {
  var options = loaderUtils.getOptions(this);
  var callback = this.async();
  setTimeout(() => {
    var result = source.replace('World', options.name);
    callback(null, result);
  }, 0);
}
1
2
3
4
5
6
7
8
9

至此,我们已经掌握了如何编写、如何引用、如何传递参数以及如何写异步代码,在下一小节当中我们将学习如何编写自己的plugin