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日

相关推荐

  • MyBatis配置之typeHandler类型处理器

    typeHandler类型处理器作用 MyBatis在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)中取出一个值时,都会用注册…

    2022年4月20日
    1.2K
  • js数组去重(区分object、“NaN”、NaN)

    数组去重在前端面试中比较常见,今天来复习复习。 对这样一个数组进行去重,我尝试了几种方法,大多数不能对对象去重,或者不能区分true和”true”、NaN和…

    2021年2月23日
    1.3K
  • MySQL数据库入门之单表数据记录查询

    简单数据查询 语法形式如下: 在上述语句中,参数fieldn表示所要查询的字段名字,参数table_name表示所要查询数据记录的表名。 当需要查询表中所有字段时,可使用&#822…

    2020年7月15日
    1.5K
  • JavaScript 事件委托详解

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

    2021年3月8日
    1.2K
  • Angular 4.x ngModel 双向绑定原理揭秘

    一直以来都没有去深入探究Angular,只是熟练运用。真要被问起来,很多关于angular的理论知识都回答不上来。感觉上学背书的能力已经丧失的差不多了。只能以这样的方式搜集整理出来。

    2019年7月2日
    1.9K
  • Java自学之多线程编程

    多线程编程是Java语言最为重要的特性之一。利用多线程技术可以提升单位时间内的程序处理性能,也是现代程序开发中高并发的主要设计模式。 进程与线程 进程是一个应用程序。线程是一个进程…

    2020年12月16日
    1.6K
  • vue3.0项目如何配置路径别名

    vue更新到3.0以后,在项目中已经深度集成了webpack,使用vue create命令新建项目之后,已经没有webpack配置文件了,这对于像小编这样没有系统学习过前端的同学来…

    2020年8月22日
    4.1K
  • 回调函数散记

    今天被将要入职的公司的开发人员询问了一个项目中遇到的问题,关于函数内访问外部函数的情况。大致现象如下:js文件中有两个同级函数FnA和FnB,想在函数FnA中调用FnB。 一看就是…

    2019年8月16日
    1.5K
  • JavaScript基础知识八问

    JavaScript是前端开发中非常重要的一门语言,浏览器是他主要运行的地方。JavaScript是一个非常有意思的语言,但是他有很多一些概念,大家经常都会忽略。比如说,原型,闭包…

    2020年12月30日
    1.0K
  • Webpack入门,常用loader

    babel-loader babel-loader用于处理ES6+并将其编译为ES5,它使开发者能够在工程中使用最新的语言特性,同时不必特别关注这些特性在不同平台的兼容问题。 在安…

    2022年11月23日
    687