深入webpack打包原理及loader和plugin的实现

深入webpack打包原理及loader和plugin的实现

一、Webpack的打包原理

Webpack 是一个现代化的 JavaScript 应用程序打包器。Webpack 会读取你的应用程序并构建一个依赖关系图,然后根据这个图创建一个打包文件。在打包的过程中,Webpack 的核心功能包括模块解析器、依赖关系解析器、代码生成器和打包器等。Webpack 的打包流程大体可以分为如下几个阶段:

  1. 初始化阶段:读取配置文件,创建Compiler对象,加载所有的插件,等等;

  2. 编译阶段:通过调用 Compiler 对象提供的 run 方法来进行编译,主要是如下几个过程:

  3. 根据 entry 和其依赖的模块,构建成 AST 树,然后经过转换器进行语法转换、附加前置、后置、中间模块等操作转换器;

  4. 根据不同类型的模块,对模块进行相应的处理(姑且称之为 load),例如: 解析模块依赖(package.json, CommonJS, ES6 等),处理样式文件、图片等文件引用等;

  5. 对模块进行封装,生成完成的 Chunk 对象;

  6. 输出阶段:对 Chunk 进行优化、压缩、最终的输出文件等操作。

下面我们重点关注在编译阶段中,webpack是如何对模块进行处理的。

二、Loader的实现

在Webpack中,Loader是一个文件转换器,它专门用于处理资源文件,将入口文件和依赖文件都作为参数传入。Webpack 会通过读取配置文件的module.rules属性,来决定用哪个Loader进行处理。其实现基本流程可以简述为:

  1. 读取配置文件,解析出module.rules下的所有正则表达式对象,并通过正则对象来匹配module下的模块文件;

  2. 根据匹配的Loader对文件进行相应的转换处理等操作;

  3. 返回转换好的内容。

有关 Loader 实现的两个示例:

1. 常用的文件转换

常用 Loader 中的一种是处理样式文件的 style-loader 和 css-loader,它们的作用是将 css 样式文件打包到 JavaScript 文件中,目的是为了提高浏览器的加载速度。

例如 css-loader 可以解析 CSS 文件,并将 CSS 文件转换成一个字符串,然后交给 style-loader 处理,style-loader 可以在 HTML 文件中插入一段 style 标签,然后将 CSS 字符串放入 style 标签中。

// css-loader.js
module.exports = function(source) {
  return JSON.stringify(source); // 将 css 转为字符串
}

// style-loader.js
module.exports = function(cssString) {
  const style = document.createElement('style');
  style.innerText = cssString; // 内联CSS
  document.head.appendChild(style); // 将style标签插入HTML
}

2. Babel转换器

常见的一个Loader是 Bable-loader,用于将 ES6 或更高的语法或其他语言转成浏览器支持的语法。

例如,将 ES6 代码转换成 ES5 代码:

// babel-loader.js
const babelCore = require('@babel/core');

module.exports = function(source) {
  const { code } = babelCore.transform(source, {
    presets: [
      ['@babel/preset-env', { targets: 'last 2 versions, >1%, not dead' }]
    ]
  });
  return code;
}

三、Plugin的实现

Plugin是可以影响Webpack的构建流程,并将在整个构建流程中“挂载”在Webpack的钩子上。Webpack的运行过程被定义为一个生命周期,而 Plugin 就是在 Webpack 生命周期中,将要被执行的代码。

常见的Plugin有:html-webpack-plugin插件可以在打包完成后,将打包生成的 js 自动引入到 html 页面中;clean-webpack-plugin插件可以在打包前自动清理输出目录;copy-webpack-plugin插件可以将需要转换成 ES5 代码的 js 文件从 dist 目录中COPY 到 dist/es5 目录中等等。

Plugin的实现基本流程可以简述为:

  1. 在Webpack生命周期中通过生命周期钩子来获得Webpack中要处理的‘资源’。

  2. 通过‘资源’创建新的‘资源’,并将这些‘资源’添加到Webpack构建流程中。

  3. 完成插件(Plugin)的生命周期

有关 Plugin 实现的两个示例:

1. html-webpack-plugin插件

样式通常会以 link 标签或者 style 标签的形式插入到 HTML 中。针对这点,我们可以实现一个插件,自动向 HTML 文件中添加 link 或 style 标签,这样在浏览器中访问时,就可以自动加载样式。

const HtmlWebpackPlugin = require('html-webpack-plugin');

class AutoInsertStylePlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync('AutoInsertStylePlugin', (compliation, callback) => {
      const htmlFileName = 'index.html'; // 设置需要插入 link 或 style 标签的HTML 文件名
      let htmlFileContent = null;

      for (const asset of compliation.assets) {
        const [fileName, fileContent] = Object.entries(asset)[0];
        if (fileName === htmlFileName) {
          htmlFileContent = fileContent.source(); // 读取 HTML 的文件内容
          break;
        }
      }

      if (!htmlFileContent) {
        callback();
        return;
      }

      const styleContent = ''; // 通过某种方式获得样式内容

      const styles = `<style>${styleContent}</style>`;
      const updatedFileContent = htmlFileContent.replace(/<\/head>/, + styles + '</head>'); // 通过正则将新的样式内容添加到 HTML 中

      compliation.assets[htmlFileName] = {
        source: () => updatedFileContent,
        size: () => updatedFileContent.length
      }

      callback();
    });
  }
}

// 使用
module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    new AutoInsertStylePlugin()
  ]
};

2. clean-webpack-plugin插件

const fs = require('fs');

class CleanWebpackPlugin {
  apply(compiler) {
    compiler.hooks.beforeRun.tapAsync('clean-webpack-plugin', (compiler, callback) => {
      try {
        fs.rmdirSync('./dist', { recursive: true }); // 递归删除 dist 目录以及其下的所有文件
      } catch (e) {
        console.error(`Error while deleting dist directory : ${e}`);
      }
      callback();
    });
  }
}

// 使用
module.exports = {
  plugins: [
    new CleanWebpackPlugin()
  ]
};

这个插件会在 Webpack 打包开始之前,将 dist 目录删除,防止不必要的文件增量构建,并在下一次构建开始时清空整个 dist 目录。

阅读剩余 73%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入webpack打包原理及loader和plugin的实现 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 详解Java中跳跃表的原理和实现

    详解Java中跳跃表的原理和实现 跳跃表的概念与特点 跳跃表是一种有序数据结构,通过维护多级索引来加快查找速度。它只能用于元素可比较的有序列表,并且支持对元素的快速访问、插入和删除操作。跳跃表的平均查找、插入和删除时间复杂度均为$O(logn)$,与平衡树的性能相当,但跳跃表比平衡树更加简单,容易实现和维护。 跳跃表的基本结构包括:1. 元素节点: 存储元素…

    other 2023年6月27日
    00
  • pid文件的作用

    PID文件是一种记录进程ID的文件,通常用于管理和监控进程。在Linux和Unix系统中,PID文件通常存储在/var/run目录下。本文将介绍PID文件的作用和使用方法。下面是PID文件的完整攻略,包括两个示例说明。 示例一:使用PID文件管理进程 在Linux和Unix系统中,PID文件通常用于管理和监控进程。下面是一个示例,用于演示如何使用PID文件管…

    other 2023年5月9日
    00
  • HTML仿命令行界面具体实现

    HTML仿命令行界面可以使用HTML、CSS和JavaScript实现,下面我将分步骤介绍具体实现方法。 1. HTML布局 首先,我们需要准备一个HTML文件,其中需要定义一个输入框和一个显示框,可以使用一个div元素来充当整个界面,如下所示: <div class="terminal"> <div class=&qu…

    other 2023年6月26日
    00
  • Win10预览版14965自制ISO镜像下载 32位/64位

    Win10预览版14965自制ISO镜像下载攻略 本攻略将详细介绍如何下载Win10预览版14965的自制ISO镜像,包括32位和64位版本。请按照以下步骤进行操作: 步骤一:准备工作 在开始之前,请确保您已经满足以下要求: 一台可靠的互联网连接的计算机。 足够的存储空间来保存ISO镜像文件。 一个可用的ISO镜像制作工具,如UltraISO或Rufus。 …

    other 2023年7月28日
    00
  • 查看linux之mysql是否安装的几种方法

    当然,我很乐意为您提供有关“查看Linux中MySQL是否安装的几种方法”的完整攻略。以下是详细的步骤和两个示例: 1. 什么是MySQL? MySQL是一种流行的开源关系型数据库管理系统,广泛用于Web应用程序的开发和管理。在Linux中,MySQL可以通过软件包管理器进行安装。 2. 查看MySQL是否安装 以下是两种查看Linux中MySQL是否安装的…

    other 2023年5月6日
    00
  • Win7系统修改文件格式(后辍)设置方法图文教程

    Win7系统修改文件格式(后缀)设置方法图文教程 在Win7系统中,修改文件格式(后缀)是一项常见的操作。通过修改文件的后缀,我们可以改变文件的类型,使其能够被不同的程序打开。下面是详细的操作步骤: 步骤一:显示文件后缀名 打开“资源管理器”(可以通过桌面上的“计算机”图标或者任务栏上的文件夹图标打开)。 在资源管理器的顶部菜单栏中,点击“查看”选项卡。 在…

    other 2023年8月5日
    00
  • Python命名空间的本质和加载顺序

    Python命名空间的本质和加载顺序攻略 Python中的命名空间是一个用于存储变量名称和其对应对象的映射关系的系统。在Python中,每个对象都存储在一个命名空间中,以便在代码中进行访问和使用。本攻略将详细讲解Python命名空间的本质和加载顺序,并提供两个示例来说明。 1. 命名空间的本质 命名空间是一个字典对象,用于存储变量名称和其对应对象的映射关系。…

    other 2023年8月8日
    00
  • java实现IP地址转换

    Java实现IP地址转换攻略 IP地址转换是将IP地址在不同的格式之间进行转换的过程。在Java中,可以使用InetAddress类来实现IP地址的转换。下面是一个详细的攻略,包含了两个示例说明。 步骤一:导入必要的类 首先,我们需要导入InetAddress类和相关的异常类。在Java中,这些类位于java.net包中。 import java.net.I…

    other 2023年7月30日
    00
合作推广
合作推广
分享本页
返回顶部