loader

loader

// 只在 test 和 文件名匹配 中使用正则表达式 // 在 include 和 exclude 中使用绝对路径数组 // 尽量避免 exclude,更倾向于使用 include

loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块 (webpack 自身只理解 JavaScript)

  1. 导出为函数的javascript模块
  2. 链式调用 把上一个loader产生的结果或资源文件放进去

加载相应的资源文件 loader webpack 根据正则表达式,来确定应该查找哪些文件,并将其提供给指定的 loader

module.rules

{ test: Condition }:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组,但这不是强制的。

{ include: Condition }:匹配特定条件。一般是提供一个字符串或者字符串数组,但这不是强制的。

{ exclude: Condition }:排除特定条件。一般是提供一个字符串或字符串数组,但这不是强制的。

module.resource

{ and: [Condition] }:必须匹配数组中的所有条件

{ or: [Condition] }:匹配数组中任何一个条件

{ not: [Condition] }:必须排除这个条件

module:{
  rules:[
    {
      test: /\.jsx?$/,//匹配条件
      include: [
        path.resolve(__dirname, "app")
      ],//test 和 include 具有相同的作用,都是必须匹配选项
      exclude: [
        path.resolve(__dirname, "app/demo-files")
      ], //不匹配选项
      use:[{
        loader:'babel-loader',//应该应用的 loader,解析匹配文件
        options/*loader的属性*/: {
                  presets: ["es2015"]
                },
      }],
      resource: { and: [ /* 条件 */ ] }, //所有条件都匹配时才匹配
      resource: [/* 条件 */],
      resource: {or:[/* 条件 */]},//任意条件匹配时匹配,默认是一个数组
      resource: {not:[/* 条件 */]},//条件不匹配时
    }
   ],
  noParse:[
   /special-library\.js$/
  ],
  // 不解析这里的模块
}
复制代码
{test:/\.css$/,use:['style-loader','css-loader']}
复制代码

loader API

loader:一个导出为函数的 JavaScript 模块。loader runner 会调用这个函数,函数的 this 上下文将由 webpack 填充 如果是单个处理结果,可以在同步模式中直接返回。 如果有多个处理结果,则必须调用 this.callback()。、 在异步模式中,必须调用 this.async(),来指示 loader runner 等待异步结果,它会返回 this.callback() 回调函数,随后 loader 必须返回 undefined 并且调用该回调函数。

//同步loader
//1.单结果
function someSyncOperaty(){

}
function someAsyncOperaty(){

}
module.exports = function(content, map, meta){
    return someSyncOperaty(content);
};
//2.多结果
module.exports = function(content, map, meta){
  this.callback(null,someSyncOperaty(content),map,meta);
  return;//当调用callback时,总返回undefined
};
//异步loader
// 1.单结果
module.exports = function(content, map, meta){
  var callback = this.async();
  someAsyncOperaty(content,function(err,result){
    if(err) return callback(err);
    callback(null,result,map,meta);
  })
}
//多结果
momdule.exports = function(content, map, meta){
  var callback = this.async();
  someAsyncOperaty(content,function(err,result,sourceMaps,meta){
    if(err) callback(err);
    callback(null,result,sourceMaps,meta);
  })
};
复制代码
  1. 'Raw' loader

    默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader。通过设置 raw,loader 可以接收原始的 Buffer。

    module.exports = function(content) {
        assert(content instanceof Buffer);
        return someSyncOperation(content);
        // 返回值也可以是一个 `Buffer`
        // 即使不是 raw loader 也没问题
    };
    module.exports.raw = true;
    复制代码
  2. Pitching loader

    loader 总是从右到左地被调用。在实际(从右到左)执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法。如果某个 loader 在 pitch 方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的 loader

  3. this.version loader API的版本号 目前是2

  4. this.context 模块所在的目录

  5. this.request 被解析出来的request字符串

  6. this.callback

    • 必须是 Error 或者 null
    • 一个 string 或者 Buffer。
    • 可选的:必须是一个可以被这个模块解析的 source map。
    • 可选的:会被 webpack 忽略,可以是任何东西(例如一些元数据)。
  7. this.async 告诉loader-runner 这个函数将会异步回调,返回 this.callback

  8. this.data 在 pitch 阶段和正常阶段之间共享的 data 对象。

  9. this.cacheable 设置是否可缓存标志的函数

    调用 this.cacheable(false) 关闭loader的缓存

  10. this.loaders 所有loader组成的数组 在pitch阶段可以写入

  11. this.loaderIndex 当前 loader 在 loader 数组中的索引。

  12. this.resource request 中的资源部分,包括 query 参数。

  13. this.resourcePath 资源文件的路径。

  14. this.resourceQuery 资源的query参数

  15. this.target 编译的目标。从配置选项中传递过来的。

  16. this.webpack 是否是由webpack编译 如果是webpack编译的 这个布尔值会被设置为真

  17. this.sourceMap 生成一个 source map

  18. this.emitWarning 发出警告this.emitWarning(warning)

  19. this.emitError 发出一个错误

  20. this.loadModule

    解析给定的 request 到一个模块,应用所有配置的 loader ,并且在回调函数中传入生成的 source 、sourceMap 和 模块实例(通常是 NormalModule 的一个实例)。

     loadModule(request: string, callback: function(err, source, sourceMap, module))
    复制代码
  21. this.resolve 解析一个request

       resolve(context: string, request: string, callback: function(err, result: string))
    复制代码
  22. this.addDependency 添加一个文件作为产生 loader 结果的依赖

  23. this.addContextDependency 添加一个文件夹作为产生loader结果的依赖

  24. this.clearDependencies 移除loader结果所有的依赖

  25. this.emitFile 产生一个文件

  26. this.fs 用于访问输入文件系统的属性

如何编写一个loader

用法准则

  • 简单易用。
  • 使用链式传递。
  • 模块化的输出。
  • 确保无状态。 在不同模块转换之间不保存状态
  • 使用 loader utilities。 loader-utils schema-utils
  • 记录 loader 的依赖。
  • 解析模块依赖关系。 根据模块的不同,可能有不同的模式指定依赖关系。1.转换成require语句 2.使用this.resolve函数解析路径
  • 提取通用代码。
  • 避免绝对路径。 loader-utils中stringifyRequest 方法,可以将绝对路径转化为相对路径。
  • 使用 peer dependencies。 把这个包作为一个 peerDependency 引入。在 package.json 中指定所需的确定版本。
      "peerDependencies": {
        "node-sass": "^4.0.0"
      }
    复制代码

链式调用loader时,他们会以相反的顺序执行,看数组的写法 从右往左或从下往上

  1. 最后的loader最早执行 会传入原始文件资源
  2. 第一个 loader 最后调用,期望值是传出 JavaScript 和 source map(可选)。
  3. 中间的 loader 执行时,会传入前一个 loader 传出的结果。

利用 loader 可以链式调用的优势。写五个简单的 loader 实现五项任务,而不是一个 loader 实现五项任务。功能隔离使 loader 更简单

loader工具库 loader-utils

最常用的工具:getOptions:获取传递给 loader 的选项 stringifyRequest 方法,可以将绝对路径转化为相对路径。 schema-utils

loader 依赖 以来一个外部资源

this.addDependency 如果一个 loader 使用外部资源 必须声明它

模块依赖

根据模块类型,可能会有不同的模式指定依赖关系。例如在 CSS 中,使用 @import 和 url(...) 语句来声明依赖。这些依赖关系应该由模块系统解析。

两种实现方式

  1. 通过把它们转化成 require 语句。 css-loader @import 语句替换为 require 其他样式文件,将 url(...) 替换为 require 引用文件。
  2. 使用 this.resolve 函数解析路径。 less-loader .less文件中的变量和混合跟踪都必须一次编译 所以不能将所有的@import 转化为 require,因此,less-loader 将 less 编译器进行了扩展,自定义路径解析逻辑,然后通过this.resolve解析

通用代码

避免在每个模块中生成通用的代码 ,你应该在 loader 中创建一个运行时文件,并生成 require 语句以引用该共享模块。

绝对路径

不要再模板代码中插入绝对路径 ,因为当根路径变化是 绝对路径也会变化

测试

  1. Jest babel-jest babel-preset-env允许import export async await
//.bebelrc
{
  "presets":[
    [
      "env",
      {
        "targets":{
          "node":4
        }
      }
    ]
  ]
}

//loader.js
import { getOptions } from 'loader-utils';

export default function loader(source) {
  const options = getOptions(this);

  source = source.replace(/\[name\]/g, options.name);

  return `export default ${ JSON.stringify(source) }`;
};
复制代码

2.使用 Node.js API 和 memory-fs 去执行 webpack 允许我们访问获取转换模块的统计数据 stats

//compiler.js
import path from 'path';
import webpack from 'webpack';
import memoryfs from 'memory-fs';

export default (fixture, options = {}) => {
  const compiler = webpack({
    context: __dirname,
    entry: `./${fixture}`,
    output: {
      path: path.resolve(__dirname),
      filename: 'bundle.js',
    },
    module: {
      rules: [{
        test: /\.txt$/,
        use: {
          loader: path.resolve(__dirname, '../src/loader.js'),
          options: {
            name: 'Alice'
          }
        }
      }]
    }
  });

  compiler.outputFileSystem = new memoryfs();

  return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      if (err) reject(err);

      resolve(stats);
    });
  });
}
复制代码
  1. 测试
//loader.test.js
import compiler from './compiler.js';

test('Inserts name and outputs JavaScript', async () => {
  const stats = await compiler('example.txt');
  const output = stats.toJson().modules[0].source;

  expect(output).toBe(`export default "Hey Alice!\\n"`);
});
复制代码

转载于:https://juejin.im/post/5b333387f265da59ab29937a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值