理解 loader 的工作流

0533771085735f6c165b4015b7946216.png

分类

loader 分成哪几类?如果指定类型?

loader 的叠加顺序 = post(后置)+inline(内联)+normal(正常)+pre(前置)。Rule.enforce 可能的值有:"pre" | "post",指定 loader 种类。没有值表示是普通 loader。还有一个额外的种类"行内 loader",loader 被应用在 import/require 行内。

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        // 直接这样配置就是normal
        'css-loader'
      ]
    },
    {
      test: /\.css$/,
      enforce: 'pre',
      use: [
        // 这样是前置pre
        'style-loader'
      ]
    },
    {
      test: /\.less$/,
      enforce: 'post',
      use: [
        // 这样是后置post
        'less-loader'
      ]
    }
  ]
}
// 这样是内联inline
import Styles from 'style-loader!css-loader?modules!./styles.css';

特殊配置

加载模块前的特殊符号是如何工作的?

符号变量含义
-!noPreAutoLoaders不要前置和普通 loader
!noAutoLoaders不要普通loader
!!noPrePostAutoLoaders不要前后置和普通 loader,只要内联 loader

内联方式,可以在 import 语句或任何 与 "import" 方法同等的引用方式 中指定 loader。使用 ! 将资源中的 loader 分开。每个部分都会相对于当前目录解析。

import Styles from 'style-loader!css-loader?modules!./styles.css';

通过为内联 import 语句添加前缀,可以覆盖 配置 中的所有 loader, preLoader 和 postLoader:

使用 ! 前缀,将禁用所有已配置的 normal loader(普通 loader)

import Styles from '!style-loader!css-loader?modules!./styles.css';

使用 !! 前缀,将禁用所有已配置的 loader(preLoader, loader, postLoader)

import Styles from '!!style-loader!css-loader?modules!./styles.css';

使用 -! 前缀,将禁用所有已配置的 preLoader 和 loader,但是不禁用 postLoaders

import Styles from '-!style-loader!css-loader?modules!./styles.css';

选项可以传递查询参数,例如 ?key=value&foo=bar,或者一个 JSON 对象,例如 ?{"key":"value","foo":"bar"}

组合

这些不同的loader是如何组合的?

还有一个额外的种类"行内 loader",loader 被应用在 import/require 行内。

所有一个接一个地进入的 loader,都有两个阶段:

1、Pitching 阶段: loader 上的 pitch 方法,按照 后置(post)、行内(inline)、普通(normal)、前置(pre) 的顺序调用。

2、Normal 阶段: loader 上的 常规方法,按照 前置(pre)、普通(normal)、行内(inline)、后置(post) 的顺序调用。模块源码的转换, 发生在这个阶段。

loader 总是 从右到左被调用。有些情况下,loader 只关心 request 后面的 元数据(metadata),并且忽略前一个 loader 的结果。在实际(从右到左)执行 loader 之前,会先 从左到右 调用 loader 上的 pitch 方法。

loader 可以通过 request 添加或者禁用内联前缀,这将影响到 pitch 和执行的顺序。

对于以下 use 配置:

module.exports = {
  //...
  module: {
    rules: [
      {
        //...
        use: ['a-loader', 'b-loader', 'c-loader'],
      },
    ],
  },
};

将会发生这些步骤:

|- a-loader `pitch`
  |- b-loader `pitch`
    |- c-loader `pitch`
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution

那么,为什么 loader 可以利用 "pitching" 阶段呢?

首先,传递给 pitch 方法的 data,在执行阶段也会暴露在 this.data 之下,并且可以用于在循环时,捕获并共享前面的信息。

module.exports = function (content) {
  return someSyncOperation(content, this.data.value);
};


module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  data.value = 42;
};

其次,如果某个 loader 在 pitch 方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的 loader。在我们上面的例子中,如果 b-loader 的 pitch 方法返回了一些东西:

module.exports = function (content) {
  return someSyncOperation(content);
};


module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  if (someCondition()) {
    return (
      'module.exports = require(' +
      JSON.stringify('-!' + remainingRequest) +
      ');'
    );
  }
};

上面的步骤将被缩短为:

|- a-loader `pitch`
  |- b-loader `pitch` returns a module
|- a-loader normal execution

loader-runner

所谓 loader 只是一个导出为函数的 JavaScript 模块。它接收上一个 loader 产生的结果或者资源文件(resource file)作为入参。也可以用多个 loader 函数组成 loader chain。compiler 需要得到最后一个 loader 产生的处理结果。这个处理结果应该是 String 或者 Buffer(被转换为一个 string)。loader-runner 是一个执行 loader chain 的模块。

const { runLoaders } = require("loader-runner")
const fs = require("fs")


runLoaders({
    resource: "/Users/didi/workspace/2021/webpack5/source.js?query", // 你要加载的资源


    loaders: ["/Users/didi/workspace/2021/webpack5/loader.js?query"], // 需要处理的loader


    context: { minimize: true }, // 保存一些状态和值


    readResource: fs.readFile.bind(fs)
}, function(err, result) {
    console.log(err, '运行错误')
  console.log(result, '运行结果')
})

经过 loader-runner 处理的结果为:

{
  result: [ "console.log('hello world')// hello world" ],
  resourceBuffer: <Buffer 63 6f 6e 73 6f 6c 65 2e 6c 6f 67 28 27 68 65 6c 6c 6f 20 77 6f 72 6c 64 27 29>,
  cacheable: true,
  fileDependencies: [ '/Users/didi/workspace/2021/webpack5/source.js' ],
  contextDependencies: [],
  missingDependencies: []
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值