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数据库基础之索引相关知识点整理

    数据库对象索引的出现,除了可以提高数据库管理系统的查找速度,而且还可以保证字段的唯一性,从而实现数据库表的完整性。 MySQL支持6种索引:普通索引、唯一索引、全文索引、单列索引、…

    2020年7月9日
    1.7K
  • Webpack入门,模块打包之CommonJS与ES6 Module的区别

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

    2022年11月12日
    910
  • 前端常见跨域解决方案

    跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 广义的跨域: 资源跳转: A链接、重定向、表单提交 资源嵌入: <link>、<scr…

    2019年3月25日
    3.3K
  • CSS中的BFC是什么

    定义 一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。它是块级盒布局出现的区域,也是浮动层元素进行交互的区域。 触…

    2022年11月5日
    590
  • MySQL数据库基础之视图及触发器相关知识点整理

    视图的操作 视图,本质上是一种虚拟表,其内容与真实的表相似,包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值形式存在。行和列数据来自自定义视图的查询所引用基本…

    2020年7月13日
    1.5K
  • STS插件mybatis-generator安装及使用

    断断续续学习Java也有好长时间了,没有师傅带,没有项目练手,学习超级慢,也很烦。视频、书籍翻看了一大推,还是没有目标。 相信滴水成海,外加条条大路通罗马,只要坚持,自己终能达成目…

    2019年12月27日
    2.4K
  • Java自学之抽象类与接口

    面向对象程序设计中,类继承的主要作用的扩充已有类的功能。子类可以根据自己的需要选择是否要覆写父类中的方法,所以一个设计完善的父类是无法对子类做出任何强制性的覆写约定。为了解决这样的…

    2020年12月7日
    1.3K
  • 如何封装VUE组件库?

    之前一直在使用Angular开发项目,也封装过Angular组件。由于种种原因,现需要转战VUE。好在本人有扎实的实战经验,结合各位网络大神整理的经验,现总结一篇关于封装VUE组件…

    2019年7月31日
    2.4K
  • MyBatis配置之typeHandler类型处理器

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

    2022年4月20日
    1.2K
  • 从零开始开发vue组件库

    前言 很早之前,就有开发一套vue组件库的想法,直到现在想法依旧只是想法。汗颜啊……此篇文章将讲述如何开发vue组件库,虽然文章标题为《从零开始开发vue组件库》,实际上是从搭建v…

    2024年6月23日
    753