其实webpack编译"模块化"的源码没那么难
我们在 webpack初体验 这篇文章中演示到,浏览器不支持 CommonJS ,在特定场景下才支持 Es Module ,而 webpack 可以将这些模块化的代码解析成浏览器可识别的语法。
那么 webpack 究竟是对模块化做了怎样的处理呢?一起来看看。
项目结构
demo
├─ src
│ ├─ utils
│ │ ├─ common_math.js
│ │ └─ esmodule_format.js
│ ├─ common_index.js
│ ├─ esmodule_index.js
│ └─ index.js
├─ index.html
├─ package.json
└─ webpack.config.js
分别以 CommonJS 和 ES Module 两种方式来进行导入导出
// common_math.js —— 使用 CommonJS 导出
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
module.exports = {
add,
sub,
};
// esmodule_format.js —— 使用 ES Module 导出
function timeFormat() {
return "2022-02-02";
}
export { timeFormat };
config文件配置
webpack.config.js 中 "mode" 默认为 "production",此时代码是经过压缩和丑化的,为了更利于阅读,我们将它设置为 "development"。

而 "development" 模式下 "devtool" 默认为 "eval",会给代码增加很多暂时我们不需要的内容,所以将 "devtool" 设置为 "source-map"(关于 "source-map",下一篇文章会详细介绍)
const path = require("path");
module.exports = {
entry: "./src/common_index.js",
mode: "development",
devtool: "source-map",
output: {
filename: "./bundle.js",
path: path.resolve(__dirname, "./dist"),
},
};
CommonJS
首先让 webpack 解析 CommonJS 语法,所以入口指定 common_index.js
// common_index.js —— 使用 CommonJS 导入
const { add, sub } = require("./utils/common_math.js");
console.log(add(20, 30));
console.log(sub(20, 30));
执行 npm run build ,为了方便阅读,将生成的 将 bundle.js 中的注释、以及自执行函数的最外层全部删除。
对于 CommonJS 的处理,主要有两个对象和一个函数
__webpack_modules__对象,用于保存文件路径和文件内容的映射关系__webpack_module_cache__对象,用于缓存已加载过的文件,key值为文件路径,value为导出的内容__webpack_require__函数,用于加载文件,将导出内容添加到 module.exports 和 exports 对象中
具体webpack编译源码
var __webpack_modules__ = {
// 定义一个对象,对象中的 key 为文件路径,value 为函数,函数中包括文件内容
"./src/utils/common_math.js": (module) => {
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
module.exports = {
add,
sub,
};
},
};
// 定义用于缓存已加载过文件的对象
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
// 从缓存对象中取当前moduleId的value
var cachedModule = __webpack_module_cache__[moduleId];
// 如果存在直接返回 exports 对象
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// 如果不存在,在缓存对象中增加 key 为 moduleId,值为 { export: {} } 的数据
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
// 通过 moduleId,执行保存在 __webpack_modules__ 的方法,并传入参数 module 对象,
// 执行方法后,会修改 module.exports 以及 exports 对象
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// 当前返回的就是 { add, sub } 这个对象
return module.exports;
}
var __webpack_exports__ = {}; // 这里没有用到
// 执行 common_math.js
const { add, sub } = __webpack_require__("./src/utils/common_math.js");
console.log(add(20, 30));
console.log(sub(20, 30));
通过以上方式,webpack 将浏览器不可识别的 CommonJS 代码编译成了浏览器可正常运行的语法
ES Module
将webpack.config.js 文件中的入口改成 esmodule_index.js。
// esmodule_index.js —— 使用 ES Module 导入
import { timeFormat } from "./utils/esmodule_format.js";
console.log(timeFormat());
再执行 npm run build,为了便于阅读,还是将 bundle.js 中的注释、以及自执行函数的最外层、严格模式的规定全部删除。
ES Module 的实现与 CommonJS 相同之处在于,它也有这两个对象和一个函数
__webpack_modules__对象,用于保存文件路径和文件内容的映射关系__webpack_module_cache__对象,用于缓存已加载过的文件,key值为文件路径,value为导出的内容__webpack_require__函数,用于加载文件,将导出内容添加到 module.exports 和 exports 对象中
但 ES Module 更为复杂一些,还存在这三个函数
__webpack_require__.d将传入的对象属性和值遍历到 exports 对象上__webpack_require__.o判断某属性是否存在于某对象中__webpack_require__.r给使用 ES Module 实现模块化的文件中 exports 对象里增加__esModule属性
具体webpack编译源码
var __webpack_modules__ = {
"./src/utils/esmodule_format.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
// 给exports增加__esModule属性
__webpack_require__.r(__webpack_exports__);
// 将 esmodule_format.js 中导出的函数 timeFormat 添加到 exports 对象中
__webpack_require__.d(__webpack_exports__, {
timeFormat: () => timeFormat,
});
function timeFormat() {
return "2022-02-02";
}
},
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.d = (exports, definition) => {
// 遍历 esmodule_format.js 中导出的对象
for (var key in definition) {
// 如果当前exports对象中不存在该属性,则复制到exports对象中
if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
// 定义exports对象,将函数作为get属性
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
}
}
};
// 判断 prop 属性是否存在于 obj 对象中
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
// 给 exports 对象增加 __esModule 属性
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module",
});
}
Object.defineProperty(exports, "__esModule", { value: true });
};
var __webpack_exports__ = {};
// __webpack_exports__ 上下两行暂时没有用到
__webpack_require__.r(__webpack_exports__);
// 使用函数将导出内容加载到 exports 和 module.exports 对象中
var _utils_esmodule_format_js__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/utils/esmodule_format.js");
// 以下代码和函数直接调用效果一致
console.log(
(0, _utils_esmodule_format_js__WEBPACK_IMPORTED_MODULE_0__.timeFormat)()
);
webpack 对于 ES Module 和 CommonJS 处理方式有些不同,CommonJS 中是直接将属性添加到 exports 对象中,而 ES Module 是通过 defineProperty 定义到该属性的 存取选择器 get 中
ES Module 和 CommonJS 混合使用
将webpack.config.js 文件中的入口改成 index.js。
// index.js
// ES Module 导出的内容 CommonJS 导入
const { timeFormat } = require( "./utils/esmodule_format.js");
// CommonJS 导出的内容 ES Module 导入
import { add, sub } from ("./utils/common_math.js");
console.log(timeFormat());
console.log(add(20, 30));
console.log(sub(20, 30));
再执行 npm run build,为了便于阅读,还是将 bundle.js 中的注释、以及自执行函数的最外层、严格模式的规定全部删除。
模块化相互引用的方式复用了 ES Module 和 CommonJS 都有的两个对象和一个函数
__webpack_modules__对象,用于保存文件路径和文件内容的映射关系__webpack_module_cache__对象,用于缓存已加载过的文件,key值为文件路径,value为导出的内容__webpack_require__函数,用于加载文件,将导出内容添加到 module.exports 和 exports 对象中
同时也保留了 ES Module 独有的这三个函数
__webpack_require__.d将传入的对象属性和值遍历到 exports 对象上__webpack_require__.o判断某属性是否存在于某对象中__webpack_require__.r给使用 ES Module 实现模块化的文件中 exports 对象里增加__esModule属性
再增加了一个函数
__webpack_require__.n定义遍历赋值为函数,并在该变量上添加a属性,值为函数
var __webpack_modules__ = {
"./src/utils/common_math.js": (module) => {
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
module.exports = {
add,
sub,
};
},
"./src/utils/esmodule_format.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
timeFormat: () => timeFormat,
});
function timeFormat() {
return "2022-02-02";
}
},
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.n = (module) => {
var getter =
module && module.__esModule ? () => module["default"] : () => module;
__webpack_require__.d(getter, { a: getter });
return getter;
};
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
}
}
};
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
var _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
"./src/utils/common_math.js"
);
// 暂时没有用到
var _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0___default =
__webpack_require__.n(_utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__);
const { timeFormat } = __webpack_require__("./src/utils/esmodule_format.js");
console.log(timeFormat());
console.log(
(0, _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__.add)(20, 30)
);
console.log(
(0, _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__.sub)(20, 30)
);
ES Module 和 CommonJS 混合使用的方式是被支持的,webpack 的处理方式就是将两者单独处理合并在一起。
以上就是webpack编译模块化文件的源码的内容,更多有关webpack的内容可以参考我其它的博文,持续更新中~
其实webpack编译"模块化"的源码没那么难的更多相关文章
- msvc2013编译qt5.6源码
1.回顾 说起到qt的编译,真是领人痛心啊,不仅编译选项繁多,而且编译时间比较久,总是能使想编译qt源码的人望而却步,呵呵...我就是其中一个,不知道从什么时候开始就想着把qt的源码编译一下,也尝试过 ...
- 在Ubuntu Server14.04上编译Android6.0源码
此前编译过Android4.4的源码,但是现在Android都到了7.0的版本,不禁让我感叹Google的步伐真心难跟上,趁这周周末时间比较充裕,于是在过去的24小时里,毅然花了9个小时编译了一把An ...
- 【转】编译Android系统源码和内核源码
原文网址:http://blog.csdn.net/jiangwei0910410003/article/details/37988637 好长时间没有写blog了,之所以没有写,主要还是工作上的事, ...
- FW 编译Android系统源码和内核源码
编译Android系统源码和内核源码 分类: Android2014-07-21 20:58 7287人阅读 评论(28) 收藏 举报 好长时间没有写blog了,之所以没有写,主要还是工作上的事,发现 ...
- 编译Android系统源码和内核源码
[日期:2016-01-11] 来源:Linux社区 作者:jiangwei [字体:大 中 小] 把我之前编译Android系统源码和内核源码的过程记录一下,因为这个过程真的是受益匪浅,看 ...
- Atitit.反编译apk android源码以及防止反编译apk
Atitit.反编译apk android源码以及防止反编译apk 1.1. Tool apk逆向助手1 1.2. 二.使用dex2jar + jd-gui 得到apk的java源码1 1.3. 用 ...
- Ubuntu 下载 & 编译 Android5.1 源码
ustc & tsinghua android srchttps://lug.ustc.edu.cn/wiki/mirrors/help/aosphttps://mirrors.tuna.ts ...
- 编译android5.0源码的
java环境 Android 5.1 用到的jdk不再是Oracle 的 jdk ,而是开源的 openjdk,在ubuntu安装好后,使用如下命令安装jdk: $sudo apt-get insta ...
- vs2008编译FileZilla客户端源码
vs2008编译FileZilla客户端源码 下载FileZilla客户端源码,下载地址https://download.filezilla-project.org/. FileZilla客户端解决方 ...
- 编译jmeter5.0源码
jmeter5.0使用过程中,遇到request或者response乱码的情况,想要一次性解决这个问题,需要编译ApacheJMeter_http.jar这个包(lib\ext文件下)里的Reques ...
随机推荐
- #Python 利用pivot_table,数据透视表进行数据分析
前面我们分享了,利用python进行数据合并和连接,但是工作中,我们往往需要对数据进一步的聚合或者运算,以求最后的数据结果. 今天我们就来学习一下利用pandas模块,对数据集进行数据透视分析. pi ...
- 2023-04-25:给定两个长度为N的数组,a[]和b[] 也就是对于每个位置i来说,有a[i]和b[i]两个属性 i a[i] b[i] j a[j] b[j] 现在想为了i,选一个最
2023-04-25:给定两个长度为N的数组,a[]和b[] 也就是对于每个位置i来说,有a[i]和b[i]两个属性 i a[i] b[i] j a[j] b[j] 现在想为了i,选一个最好的j位置, ...
- 2022-12-28:有n个黑白棋子,它们的一面是黑色,一面是白色, 它们被排成一行,位置0~n-1上。一开始所有的棋子都是黑色向上, 一共有q次操作,每次操作将位置标号在区间[L,R]内的所有棋子翻
2022-12-28:有n个黑白棋子,它们的一面是黑色,一面是白色, 它们被排成一行,位置0~n-1上.一开始所有的棋子都是黑色向上, 一共有q次操作,每次操作将位置标号在区间[L,R]内的所有棋子翻 ...
- pg数据库的备份和恢复以及sql脚本错误的解决方法
1.备份单库单表的数据,以insert语句的方式 pg_dump -h IP -p 端口 -U 用户名 -t 表名 --inserts –f dbname.sql 数据库名 pg_dump -h 17 ...
- 2021-03-05:go中,io密集型的应用,比如有很多文件io,磁盘io,网络io,调大GOMAXPROCS,会不会对性能有帮助?为什么?
2021-03-05:go中,io密集型的应用,比如有很多文件io,磁盘io,网络io,调大GOMAXPROCS,会不会对性能有帮助?为什么? 福哥答案2021-03-05: 这是面试中被问到的.实力 ...
- SpringBoot源码学习4——SpringBoot内嵌Tomcat启动流程源码分析
系列文章目录和关于我 零丶引入 我在初学spring的时候,很懵逼,因为整个项目中不存在main方法,让我有点摸不着头脑.那时候我知道有个东西叫tomcat是它监听了端口,解析了协议调到了我的serv ...
- hadoop 2.7.7 ERROR datanode.DataNode: BlockSender.sendChunks() exception: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
最近在测试Hbase在windows上的单机版的功能. 版本:hadoop 2.7.7 hbase 2.0.0 错误: ERROR datanode.DataNode: BlockSender.se ...
- 中文环境下使用 huggingface 模型替换 OpenAI的Embedding 接口
OpenAI的文本嵌入衡量文本字符串的相关性.嵌入通常用于: 搜索(其中结果按与查询字符串的相关性排名) 聚类(其中文本字符串按相似性分组) 推荐(推荐具有相关文本字符串的项目) 异常检测(识别出相关 ...
- java通用xls导出设计
背景 在后端日常开发中总会有各种各样的导出需求,实现这个需求必须要解决的两个问题: 1.表头不能直接使用字段名,需要显示为中文,甚至还需要考虑国际化 2.值需要翻译,比如性别.状态之类的字段 现状 现 ...
- 读少写多的条件下 ConcurrentHashMap 和 ReadWriteLock 的选择
场景是这样的:两个对象往一个 Map 里循环写入,另外一个对象偶尔读一次,写的频率比读的频率高很多.希望实现的是读的时候暂停写入.CocurrentHashMap 和 ReadWriteLock 各有 ...