webpack3.0加载器loader配置及编写(二)

发表于2018-02-05 18:04 阅读(228)

loader实际上是导出function的节点模块。当资源应该由此loader转换时,调用此函数。在简单的情况下,当只有一个loader应用于资源时,调用loader有一个参数:作为字符串的资源文件的内容。

先编写一个最简单的对.text文件类型处理的/loaders/text-loader.js:

module.exports = function(source){
    return 'module.exports = ' + JSON.stringify(source);//这里为何会对source字符串再次JSON.stringify化,最后会讲到
};

按照前一篇文章所讲的对webapck.config.js进行text-loader配置:

resolveLoader: {
  modules: [
    'node_modules',
    path.resolve(__dirname, 'loaders')
  ]
},
module: {
    rules: [
        {
          test: /\.text$/,
          loader: 'text-loader',
          options:{
              name:1
          }
        }
    ]
}

如果项目中有一个a.text内容为:

hello world!

然后在项目中就可以通过require引入该文件进行读取:

//main.js 假设a.text与main.js在同一目录下
var text = require('./a.text');console.log(text) //hello world!

此时不出意外的话,我们就成功完成了一个loader的编写。

那么为何我们引入text-loader之后通过require就可以获取到text的值呢?我的理解是在项目打包编译的过程中当遇到require('./a.text')时,因为符合text-loader的正则匹配规则,webpack会通过node File API读取a.text的内容,然后执行text-loader函数,函数的参数值为读取的内容"hello world",然后通过eval解析text-loader函数的返回值,形如:

eval("module.exports =\" hello world!\"")

所以通过require('./a.text')返回的就是"hello world!",如果在text-loader函数中不对source JSON.stringify化的话,就会出现问题:

module.exports = "hello world!"//JSON.stringify化之后,返回的才是真正的字符串 
module.exports = hello world! //如果不JSON.stringify化的话,这样的结果是不正确的

loader 工具库

编写loader时应充分利用 loader-utils 包。它提供了许多有用的工具,但最常用的一种工具是获取传递给 loader 的选项。这里有一个简单的例子:

import { getOptions } from 'loader-utils';
module.exports = function(source) {
   const options = getOptions(this);
   console.log(options);//{name:1}
   return 'module.exports = ' + JSON.stringify(source);
};

链式(Chaining)

利用 loader 可以链式调用的优势。写五个简单的 loader 实现五项任务,而不是一个 loader 实现五项任务。功能隔离不仅使 loader 更简单,也让 loader 可以使用自己本身不具备的功能。

比如我这里把text-loader简单的分解成两个loader:

//text-loader
module.exports = function(source){
    return 'module.exports = ' + source;
};
//json-stringify-loader
module.exports = function(source){
    return JSON.stringify(source);
};

webpack配置也要相应修改,

module: {
    rules: [
        {
          test: /\.text$/,
          loader: 'text-loader'
        },
        {
          test: /\.text$/,
          loader: 'json-stringify-loader'
        }
    ]
}

因为链式loader遵循自下向上或自右向左的执行规则,所以返回值为“module.exports =.....”的应该在最上边或最左边。

链式loader的执行顺序规则为:链式loader的开端loader(最右或最下的loader)首先接受一个source字符串值并且执行,返回一个处理后的值,传给倒数第二个loader的参数并执行,以此类推,直到链式loader结尾(最左或最上的loader),返回一个"module.exports = ............"。

loader 可以被链式调用意味着不一定要输出 JavaScript。只要下一个 loader 可以处理这个输出,这个 loader 就可以返回任意类型的模块。

在更复杂的情况下,链式loader 也可以通过使用 this.callback(err, value1,value2,value3) 函数,返回下一个loader任意数量的值

//text-loader
module.exports = function(source,arg){
    console.log(arg);//参数2
    return 'module.exports = ' + source;
};
//json-stringify-loader
module.exports = function(source){
    return this.callback(null,JSON.stringify(source),"参数2")
}

loader 依赖(Loader Dependencies)

如果一个 loader 使用外部资源(例如,从文件系统读取),必须声明它。这些信息用于使缓存 loaders 无效,以及在观察模式(watch mode)下重编译。下面是一个简单示例,说明如何使用 addDependency 方法实现上述声明:

import path from 'path';
module.exports = function(source) {
  var callback = this.async();
  var headerPath = path.resolve('header.js');

  this.addDependency(headerPath);

  fs.readFile(headerPath, 'utf-8', function(err, header) {
    if(err) return callback(err);
    callback(null, header + "\n" + source);
  });
};

以上就是最基本的loader编写规则,进阶请见https://doc.webpack-china.org/contribute/writing-a-loader/

webpack3.0加载器loader配置及编写(一)