前端模块化-探究webpack loader的原理以及实现常见的loader

前言

本节主要介绍这些插件的基本原理并手写一些常用的 Loader。

本节对应的 demo 可以在这里找到。

什么是 Loader

在 Webpack 中,Loader 是用于对模块的源代码进行转换的工具。Webpack 将一切视为模块,而这些模块可能是各种类型的文件,如 JavaScript、CSS、图片等。Loader 负责处理这些文件,将它们转换成 Webpack 可以处理的模块。

常用的 Loader 有:

Loader 作用
babel-loader 将现代 JavaScript 转译为较旧版本
style-loader 将样式注入到 DOM 中
css-loader 在 JavaScript/TypeScript 中解析和导入 CSS 文件
file-loader 将文件复制到输出目录
url-loader 类似于 file-loader,但可以将小文件转换为 Data URL
sass-loader 将 SASS/SCSS 文件加载并编译为 CSS
less-loader 将 LESS 文件加载并编译为 CSS
ts-loader 将 TypeScript 转译为 JavaScript
postcss-loader 使用 PostCSS 插件处理 CSS
eslint-loader 在 JavaScript/TypeScript 文件上运行 ESLint
vue-loader 加载和编译 Vue.js 组件
raw-loader 将文件内容作为字符串加载
image-webpack-loader 优化和压缩图像文件

完整的列表可以参考 Webpack 官方文档

Loader 与 Plugin 的区别

作用 工作方式 示例
Loader 用于处理模块文件,将它们转换成可以被添加到依赖图中的有效 JavaScript 代码。 沿着文件的加载链应用,按照规定的顺序一个接一个地处理模块文件。 Babel Loader 用于将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 版本。
Plugin 用于执行更广泛范围的任务,例如打包优化、资源管理、注入环境变量等。 通过钩子机制与 Webpack 构建过程的不同阶段交互,允许你在构建流程中执行自定义操作。 HtmlWebpackPlugin 用于生成 HTML 文件,并自动将打包后的脚本文件引入 HTML 中。

简而言之,Loader 处理模块文件的转换,而 Plugin 用于执行各种构建过程的自定义任务。Loader 是一个文件级别的处理器,而 Plugin 更关注整个构建流程。在配置文件中,我们会配置一系列 Loader 来处理特定类型的文件,而 Plugin 通常是一个实例,通过 plugins 数组添加到配置中。

Loader 的输入和输出

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

// loader.js
module.exports = function (content) {
   
  // 对输入源进行处理,这里简单地在源代码前面添加一行注释
  const processedSource = `// This is a custom loader\n${
     source}`;

  // 返回处理后的结果
  return processedSource;
};

module.exports.raw = true;

loader 的输出内容必须是 String 或 Buffer 类型。

同步 Loader 和异步 Loader

同步 Loader

同步 loader 是最常见的 loader 类型。它们按照 webpack 的默认处理流程,依次处理每个模块。每个同步 loader 都会在一个模块的代码被转换之后,再将结果传递给下一个 loader。这种 loader 的编写和配置非常简单。

异步 Loader

异步 loader 具有更灵活的处理方式,它们可以在处理一个模块时进行一些异步操作,例如从网络请求数据,然后在异步操作完成后继续处理模块。

为了创建异步 loader,需要使用 this.async() 方法。这个方法返回一个回调函数,当异步操作完成时,你需要手动调用这个回调函数,将处理结果传递给下一个 loader。

以下是一个简单的异步 loader 的例子,假设它通过网络请求获取模块内容:

// async-loader.js
module.exports = function (source) {
   
  // 获取 loader 上下文,this 对象
  const callback = this.async();

  // 模拟一个异步操作(例如,从网络请求数据)
  setTimeout(() => {
   
    const simulatedData = {
    message: "这是模拟的异步数据" };
    const transformedSource = source.replace(
      "/* async-data-placeholder */",
      JSON.stringify(simulatedData)
    );

    // 调用回调函数,将处理后的源代码传递给下一个 loader
    callback(null, transformedSource);
  }, 1000); // 模拟异步操作耗时 1 秒钟
};

Loader 的工作原理和执行顺序

在 Webpack Loader 的执行过程中,有两个重要的阶段, pitch 阶段和 normal 阶段。

假设我们有以下三个 Loader:Loader1、Loader2、Loader3,对应的配置如下:

// webpack.config.js
module: {
   
  rules: [
    {
   
      test: /\.js$/,
      use: ['loader3', 'loader2', 'loader1'],
    },
  ],
}

对应的 webpack 执行阶段如下图

Pitch 阶段:

  • 如果存在 pitch 方法:

    • 首先执行 loader3 的 pitch 方法。
    • 接着执行 loader2 的 pitch 方法。
    • 最后执行 loader1 的 pitch 方法。
      Normal 阶段:
  • 如果存在 normal 方法:

    • 首先执行 loader1 的 normal 方法。
    • 接着执行 loader2 的 normal 方法。
    • 最后执行 loader3 的 normal 方法。

enforce 属性的作用:

enforce: “pre” 属性将 loader1 设置为预处理 Loader,这意味着在正常的 Loader 执行前,会先执行 loader1 的 pitch 方法和 normal 方法。这用于在正式加载模块之前执行一些预处理操作,例如代码静态分析或代码风格检查。

Pitch 阶段的作用:

如果某个 Loader 的 pitch 方法返回了非 undefined、非 null 或非空字符串的结果,Webpack 将停止执行 Pitch 阶段,并从该 Loader 开始执行 Normal 阶段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值