Webpack入门,模块打包原理分析

面对工程中成百上千个模块,Webpack究竟是如何将它们有序地组织在一起,并按照开发者预想的顺序运行在浏览器上的呢?本篇文章将通过一个简单的示例。分析一下Webpack模块打包的原理。

// example.js
module.exports = {
  add: function(a,b) {
    return a + b;
  }
}
// index.js
const example = require("./example.js");
const sum = example.add(5,6);
console.log("sum:",sum);

上面的代码经过Webpack打包后将会成为如下的形式(为了易读性,删除了些注释的代码):


(() => {
  var __webpack_modules__ = ({
    "./src/example.js": ((module) => {
      eval("module.exports = {\r\n  add: function (a, b) {\r\n    return a + b;\r\n  },\r\n};\r\n\n\n//# sourceURL=webpack://webpack-demo/./src/example.js?");
}),

    "./src/index.js":((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
      eval("const example = __webpack_require__(/*! ./example.js */ \"./src/example.js\");\r\nconst sum = example.add(5, 6);\r\nconsole.log(\"sum:\", sum);\r\n\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
 })
});

// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {
	// Check if module is in cache
	var cachedModule = __webpack_module_cache__[moduleId];
	if (cachedModule !== undefined) {
		return cachedModule.exports;
	}
	// Create a new module (and put it into the cache)
	var module = __webpack_module_cache__[moduleId] = {
		// no module.id needed
		// no module.loaded needed
		exports: {}
	};

	// Execute the module function
	__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

	// Return the exports of the module
	return module.exports;
}

var __webpack_exports__ = __webpack_require__("./src/index.js");

})();

这是一个最简单的Webpack打包结果。但是已经可以清晰地展示出它如何将具有依赖关系的模块串联在一起的。

上面的打包结果分为以下几个部分。

  • 最外层匿名函数。它用来包裹整个打包结果,并构成自身的作用域。
  • __webpack_modules__对象。工程中所有产生了依赖关系的模块都会以key-value的形式放在这里。key可以理解为一个模块的id,由数字或者一个很短的hash字符串构成;value则是由一个匿名函数包裹的模块实体,匿名函数的参数赋予了每个模块导出和导入的能力。
  • __webpack_module_cache__对象。每个模块只在第一次被加载的时候执行,之后其导出值就被存储到这个对象里面,当再次被加载的时候webpack会直接从这里取值,而不会重新执行该模块。
  • __webpack_require__函数。对模块加载的实现,在浏览器中可以通过调用__webpack_require__(moduleId)来完成模块的导入。
  • __webpack_exports__对象。它的作用是加载入口模块并返回导出值。

下面分析一下,打包的结果如何在浏览器中执行的。

  1. 在最外层匿名函数中初始化浏览器执行环境,包括定义__webpack_module_cache__对象、__webpack_require__函数等,为模块的加载和执行做一些准备工作。
  2. 加载入口模块。每个打包结果有且仅有一个入口哦模块,在上面的示例中,index.js是入口模块,在浏览器中会从它开始执行。
  3. 执行模块代码。如果执行到了module.exports则记录下模块的导出值;如果中间遇到require函数(即__webpack_require__),则会暂时交出执行权,进入__webpack_require__函数体内进行加载其他模块的逻辑。
  4. 在__webpack_require__中判断即将加载的模块是否存在于__webpack_module_cache__中。如果存在则直接取值,否则执行该模块的代码获取导出值。
  5. 所有依赖的模块都已经执行完毕,最后执行权又回到入口模块。当入口模块的代码执行完毕,也就意味着整个文件运行结束。

提示

通过分析,可以看出,代码运行的第三步和第四步是一个递归的过程。Webpack为每个模块创造了一个可以导出和导入的环境,但本质上并没有修改代码的执行逻辑,因此代码执行的顺序与模块加载的顺序是完全一致的,这也是Webpack模块打包额奥秘。

示例代码仓库

https://gitee.com/zero_79152105/webpack-vblog

原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-module-bundle.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZEROZERO
上一篇 2022年11月14日
下一篇 2022年11月18日

相关推荐

  • MySQL数据库入门之数据的操作知识点整理

    插入数据记录 插入一条完整数据记录 在上述语句中,参数table_name表示所要插入完整记录的表名,参数fieldn表示表中全部的字段名字,参数valuen表示所要插入的数值,最…

    2020年7月14日
    1.4K
  • Webpack入门,模块打包之CommonJS简介

    CommonJS 说到前端的模块,不得不讲一下CommonJS。CommonJS是由JavaScript社区于2009年提出的包含模块、文件、IO、控制台在内的一系列标准。Node…

    2022年11月10日
    501
  • Webpack入门,模块打包之CommonJS与ES6 Module的区别

    前面几篇文章分别介绍了CommonJS和ES6 Module两种形式的模块定义,这篇将介绍下两者各自的特性。 动态与静态 CommonJS与ES6 Module最本质的区别在于前者…

    2022年11月12日
    756
  • JavaScript 事件委托详解

    基本概念 事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown……)的函数委托到另一个元素; 一般来讲,会把一个或者一组元素的事件委托到…

    2021年3月8日
    1.1K
  • 如何封装JDBC工具类读取properties配置文件连接数据库

    思路 之前已经写过一片《封装JDBC工具类,连接MySQL数据库》,讲解了如何封装JDBC工具类。但是在实际的应用中往往是把数据库连接的相关信息写在一个配置文件中,让程序自己去读取…

    2022年4月2日
    829
  • windows 11 如何安装Docker Desktop

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化…

    2022年10月27日
    486
  • MyBatis之MyBatis-Generator标签配置及意义

    DTD 标签 <generatorConfiguration/>: 根标签,所有的配置都必须在该标签内配置;没有属性 <properties/>: 主要引用外部的pro…

    2019年12月27日
    1.8K
  • Webpack入门,自定义loader

    有时开发者会遇到现有loader无法很好满足需求的情况,这时就需要对其进行修改,或者编写新的loader。 下面以一个简单的示例,讲解以下如何实现一个loader。 需求 实现一个…

    2022年11月23日
    704
  • 创建vue3 + typescript项目说明

    创建项目 选择Manually select features【手动配置】 如上图选择配置项,并在下一步,选择3.x 接下来,按照推荐设置,进行选择【推荐设置都已英文字母大写的形式…

    2023年11月23日
    551
  • 如何搭建MyBatis开发环境

    进入一段时间的学习及温习,已经可以说是初步掌握了Javaweb入门开发,由于我的中心思想是抛弃JSP,做纯粹的前后端分离项目,所以接下来计划学习持久层开发,现在主流的持久层开发工具…

    2022年4月6日
    741