共计 7854 个字符,预计需要花费 20 分钟才能阅读完成。
咱们在 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 的内容能够参考我其它的博文,继续更新中~