likes
comments
collection
share

webpack 中的 loader 和 plugins 区别你真的理解了吗?在 Webpack 中,loader 和 p

作者站长头像
站长
· 阅读数 20

在 Webpack 中,loader 和 plugins 是两种强大的机制,用于处理模块和扩展 Webpack 的功能,但它们在应用和功能上有着本质的区别。

Loader

Loader 专门用于转换某些类型的模块。Webpack 本身只能理解 JavaScript,但 Loader 可以将各种类型的文件转换为有效的模块,供你的应用程序使用以及添加到依赖图中。简单来说,loader 在模块的加载和解析过程中工作。

  • 作用:主要用于对模块的源代码进行转换。Loader 可以链式传递,每个 Loader 通过对资源进行转换,然后将转换后的结果传递给下一个 Loader,直到所有 Loader 完成处理。

  • 用例:包括将 TypeScript 转换为 JavaScript、将 SCSS 转换为 CSS、将图片优化、将 JSX 转换为 JavaScript 等。

例如下面的配置:

module: {
  rules: [
    { test: /\.tsx?$/, loader: "ts-loader" },
    { test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader"] },
  ];
}

在这个示例中,.tsx 文件将通过 ts-loader 被转换为 JavaScript,而 .scss 文件会先通过 sass-loader 处理成 CSS,然后 css-loader 将 CSS 转换成 JavaScript 模块,最后 style-loader 将样式注入到 DOM 中。

常见的 loader 有什么

babel-loader:它的作用是将 ES6+ 的代码转换为向后兼容的 JavaScript 代码,以兼容当前和旧版本的浏览器。

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
        },
      },
    },
  ];
}

css-loader:它的作用是解析 CSS 文件中的 @import 和 url() 为 import/require() 方式,并将 CSS 转换为 JavaScript。

module: {
  rules: [
    {
      test: /\.css$/,
      use: ["style-loader", "css-loader"],
    },
  ];
}

style-loader:它的作用是将模块的输出“注入”到 DOM 中的 <style> 标签里,常与 css-loader 结合使用.

sass-loader:主要是将 SASS/SCSS 文件转换为 CSS。

这些 loader 都是为了优化开发和构建流程,使得源代码和资源能够更适合 Web 环境的需要。配置它们需要在 Webpack 的配置文件中设定 rules,根据不同文件类型指定相应的 loader。这样,当 Webpack 处理应用程序时,它会自动应用这些 loader 来处理文件。

自定义 loader

要在 Webpack 中编写自定义 loader,您需要遵循几个基本步骤来创建一个能够接收源文件内容、处理这些内容,并返回新内容的 Node.js 模块。以下是编写自定义 Webpack loader 的详细步骤:

首先我们需要创建一个 loader 文件,它应该是一个 NodeJs 模块,这个模块需要导出一个函数,这个函数接收源内容作为参数,执行所需的转换,然后返回新的内容或抛出一个错误。

这个函数将接收至少一个参数(源文件的内容)以及一个可选的 SourceMap。您可以在这个函数内部使用任何 JavaScript 库或自定义逻辑来处理输入内容。

接下来我们要编写的一个是将 js 文件里面的特定内容进行转换,如下代码所示:

module.exports = function (source) {
  const options = this.getOptions() || {};
  const replacements = options.replacements || [];

  replacements.forEach((replacement) => {
    source = source.replace(
      new RegExp(replacement.pattern, "g"),
      replacement.replacement
    );
  });

  return source;
};

上面的这些代码,它将接受一个配置对象,该对象包含用于替换的模式(正则表达式)和替换值。

接下来,需要在 webpack.config.js 中配置这个新的 Loader。假设你希望应用这个 Loader 到所有的.js 文件,可以这样配置:

接下来我们就可以在 webpack.config.js 中配置这个新的 loader,如下所示:

const { SRC_PATH } = require("./util/constants");

const path = require("path");

module.exports = {
  stats: "errors-only",
  entry: {
    index: path.join(SRC_PATH, "index.js"),
  },
  output: {
    filename: "bundle.js",
    path: path.resolve(process.cwd(), "dist"),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-env"],
            },
          },
          {
            loader: path.resolve("./loader/advanced-replace-loader.js"),
            options: {
              replacements: [
                { pattern: "Hello", replacement: "你好鸭" },
                { pattern: "World", replacement: "我是Moment" },
              ],
            },
          },
        ],
      },
    ],
  },
};

这个配置将 advanced-replace-loader 应用于.js 文件,replacements 选项中定义了多个替换规则。这样,每个匹配的字符串都将被替换。

这个时候我们在 src/index.js 文件下编写如下代码:

console.log("Hello, World!");

console.log("不是所有的牛奶都叫特仑苏");

运行 Webpack 后,这段代码会转换为如下图所示:

webpack 中的 loader 和 plugins 区别你真的理解了吗?在 Webpack 中,loader 和 p

通过这个示例,你可以看到如何创建一个能够接受多种替换规则的更复杂的 Webpack loader。根据需要,你可以扩展此 loader 以支持更多的功能,比如支持文件类型过滤、错误处理机制、性能优化等。

Plugins

Plugins 在 Webpack 构建流程中的不同阶段广泛参与,用于执行范围更广的任务。Plugins 可以影响构建流程的每个阶段,并且它们的功能不仅限于资源的加载和转换。

它的主要作用是用于执行除了资源转换之外的任何其他构建任务,比如优化、环境变量注入、代码压缩等等。;例如生成 HTML 文件、清理构建目录、压缩打包后的文件、热模块替换等等。

例如下面的例子:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

plugins: [
  new HtmlWebpackPlugin({ template: "./src/index.html" }),
  new CleanWebpackPlugin(),
];

在这个示例中,HtmlWebpackPlugin 自动生成一个 HTML 文件并注入所有生成的 bundle,CleanWebpackPlugin 在每次构建前清理输出目录,确保构建产物不会包含过时的文件。

如何自定义 Plugins

在 Webpack 中创建自定义插件可以使你能够在构建过程中的特定时间点执行自定义的操作。Webpack 的插件系统非常强大,允许开发者介入构建流程的几乎每一个阶段。

下面,我们将你创建一个简单的自定义 Webpack 插件,该插件在编译开始和结束时记录时间,帮助开发者了解构建过程的耗时。

首先我们创建一个名为 TimeLoggerPlugin.js 的文件。在这个文件中,我们将定义一个类,该类包含一个 apply 方法。这是所有 Webpack 插件必须实现的方法,Webpack 会调用它来安装插件。

在 TimeLoggerPlugin.js 中,编写以下代码:

class TimeLoggerPlugin {
  /**
   * @param {any} options
   */
  constructor(options) {
    // 可以接受配置选项
    this.options = options;

    console.log("传递进来的参数", this.options);
  }

  /**
   * @param {{ hooks: { compile: { tap: (arg0: string, arg1: () => void) => void; }; done: { tap: (arg0: string, arg1: (stats: { endTime: number; startTime: number; }) => void) => void; }; }; }} compiler
   */
  apply(compiler) {
    // 挂载到 compilation 钩子上,以便在编译(compilation)开始时触发
    compiler.hooks.compile.tap("TimeLoggerPlugin", () => {
      console.log("编译开始...");
      console.log(`开始时间: ${new Date().toLocaleString()}`); // 使用本地时间格式
    });

    // 挂载到 done 钩子上,当一切完成时触发
    compiler.hooks.done.tap(
      "TimeLoggerPlugin",
      (/** @type {{ endTime: number; startTime: number; }} */ stats) => {
        console.log(`结束时间: ${new Date().toLocaleString()}`); // 使用本地时间格式
        console.log("编译已完成!");
        console.log(`持续时间: ${stats.endTime - stats.startTime}ms`);

        console.log("现在你可以查看构建报告,优化你的应用性能。");
        if (this.options && this.options.analyze) {
          console.log("构建分析已启用,可以通过分析报告来了解更多细节。");
        }
      }
    );
  }
}

module.exports = TimeLoggerPlugin;

这个插件通过 compiler.hooks 接口访问 Webpack 的事件系统。compile 钩子在编译开始时触发,而 done 钩子在所有任务完成时触发。

在 Webpack 插件中,apply 方法是一个特别重要的部分,它是每个 Webpack 插件必须实现的方法。Webpack 在运行时会自动调用这个方法,并传入一个 compiler 对象作为参数。这个 compiler 对象是 Webpack 的主要引擎,它包含了整个编译环境的所有配置信息以及大部分可用的方法,允许插件通过注册钩子函数来访问编译过程中的特定事件。

在我们的 webpack.config.js 文件中引入并使用这个插件:

const { SRC_PATH } = require("./util/constants");
const TimeLoggerPlugin = require("../loader/TimeLoggerPlugin");

const path = require("path");

module.exports = {
  stats: "errors-only",
  entry: {
    index: path.join(SRC_PATH, "index.js"),
  },
  output: {
    filename: "bundle.js",
    path: path.resolve(process.cwd(), "dist"),
  },

  plugins: [new TimeLoggerPlugin({ m: "靓仔" })],
};

现在当我们运行 webpack 进行打包的时候,它会在我们控制台上显示编译开始的时间和结束的事件,以及整个过程的持续时间。

webpack 中的 loader 和 plugins 区别你真的理解了吗?在 Webpack 中,loader 和 p

你可以看到,我们在调用的时候传入的参数是可以被构造方法接收到的,那么我们就可以根据这个参数来做一些其他的事情了。

你可以根据需要扩展这个插件,比如添加更多的日志信息、分析构建的具体部分,或者对某些条件下的构建进行特殊处理。插件的能力非常强大,几乎可以让你在构建过程中的任何时间点执行代码。

通过实现和使用自定义 Webpack 插件,你可以有效地控制和扩展你的构建流程,实现特定的业务或技术需求。

Loader 和 Plugins 的区别

在 Webpack 中,loader 和 plugins 的作用域主要体现在它们在构建过程中的作用和影响范围。

Loader 的作用域是模块级别的。它们对单独的文件或模块进行操作。每个 loader 只关心通过其测试条件(通常是文件扩展名或特定的文件路径模式)匹配的单个文件。这意味着 loader 的作用是局部和独立的,专注于文件转换任务。

  • 局部作用:Loader 在模块的加载和解析过程中工作,只影响那些匹配特定测试条件的文件。例如,babel-loader 只会作用于符合其正则表达式测试条件(如所有 .js 文件)的文件,并将 ES6+ JavaScript 代码转换为兼容老版本浏览器的 ES5 代码。

  • 链式操作:Loader 可以链式使用,其中每个 loader 只处理前一个 loader 输出的结果。这种链式工作方式让每个 loader 都专注于一种转换,简化了管理和维护。

Plugins 的作用域是构建过程级别的。它们能够接触和影响到整个构建过程。Webpack 提供了许多钩子,允许 plugins 在构建的各个阶段执行操作,如启动、生成资源、优化等。

  • 全局作用:Plugins 可以作用于构建流程的任何部分,不限于单一的文件或模块。它们可以访问、修改甚至替换 webpack 的内部数据结构。

  • 多功能性:Plugins 能执行各种任务,包括但不限于资源管理、环境变量注入、代码压缩、生成附加文件等。例如,HtmlWebpackPlugin 可以生成 HTML 文件并自动注入所有打包后的资源,DefinePlugin 可以在编译时向代码中注入全局常量。

Loader 和 Plugins 在 Webpack 构建过程中有不同的作用域和职责:

  • Loader 处理文件级别的输入,通过转换文件内容,将非 JavaScript 文件集成到 Webpack 构建中。

  • Plugins 影响和操作整个构建流程,通过在构建的不同阶段执行任务来优化和调整输出结果。

这种设计让 Webpack 非常灵活和强大,能够通过配置不同的 loader 和 plugins 来满足广泛的开发需求和构建优化。

总结

Loader 是模块级别的,处理文件的转换和添加到依赖图中。

Plugins 是构建流程级别的,能够处理几乎所有的任务,并且可以自定义构建产物和构建过程。

通过结合使用 Loader 和 Plugins,Webpack 提供了一种非常灵活和强大的方式来处理前端资源,并优化项目构建流程。

转载自:https://juejin.cn/post/7368313344711933979
评论
请登录