为什么选择 Webpack
想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们是如何在 web 中使用 JavaScript 的。
在浏览器中运行 JavaScript 有两种方法。第一种方式,引用一些脚本来存放每个功能;此解决方案很难扩展,因为加载太多脚本会导致网络瓶颈。第二种方式,使用一个包含所有项目代码的大型 .js 文件,但是这会导致作用域、文件大小、可读性和可维护性方面的问题。
来自 Web 项目的好消息是,模块正在成为 ECMAScript 标准的官方功能。然而,浏览器支持不完整,版本迭代速度也不够快,目前还是推荐上面那些早期模块实现。
是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式(至少在我们到达 ESM 之前),并且可以同时处理资源和资产?
这就是 webpack 存在的原因。它是一个工具,可以打包你的 JavaScript 应用程序(支持 ESM 和 CommonJS)。
Webpack 构建出来的产物是什么
通过构建产物我们分析以下三个概念
- 模块注册器:webpack_modules
- 模块缓存器:webpack_module_cache
- 模块加载器:webpack_require
首先看下 CommonJS 模式
// count.js 文件
const count = 10;
exports.count = count;
// list.js 文件
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
module.exports = {
list,
};
// index.js 文件
const list = require('./course/list');
const count = require('./course/count');
console.log(list);
console.log(count);
// 打包后的产物
(() => {
const __webpack_modules__ = ({
'./src/course/count.js':
((__unused_webpack_module, exports) => {
const count = 10;
exports.count = count
}),
'./src/course/list.js':
((module) => {
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
module.exports = {
list,
}
})
});
const __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
const cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
const module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
const __webpack_exports__ = {};
(() => {
const list = __webpack_require__('./src/course/list.js')
const count = __webpack_require__('./src/course/count.js')
console.log(list)
console.log(count)
})();
})();
可以看到 webpack_modules 和 webpack_module_cache 是一个以文件路径为 key, 文件内容为 value 的对象,webpack_require 是以文件路径为参数获取文件内容的函数。我们使用模块的时候也就是通过 moduleId 来加载模块文件。
再来看下 ESM 模式
// count.js 文件
const count = 10;
export default count;
// list.js 文件
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
export default list;
// index.js 文件
import list from './course/list';
import count from './course/count';
console.log(list);
console.log(count);
// 打包后的产物
(() => {
const __webpack_modules__ = ({
'./src/course/count.js':
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
'default': () => (__WEBPACK_DEFAULT_EXPORT__)
});
const count = 10;
const __WEBPACK_DEFAULT_EXPORT__ = (count);
}),
'./src/course/list.js':
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
'default': () => (__WEBPACK_DEFAULT_EXPORT__)
});
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const __WEBPACK_DEFAULT_EXPORT__ = (list);
})
});
const __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
const cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
const module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
(() => {
__webpack_require__.d = (exports, definition) => {
for (const 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 });
};
})();
const __webpack_exports__ = {};
(() => {
__webpack_require__.r(__webpack_exports__);
const _course_list__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__('./src/course/list.js');
const _course_count__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__('./src/course/count.js');
console.log(_course_list__WEBPACK_IMPORTED_MODULE_0__['default']);
console.log(_course_count__WEBPACK_IMPORTED_MODULE_1__['default']);
})();
})();
可以看到 ESM 模式是在通过,首先在注册模块阶段通过 webpack_require.r 打上 __esModule 以及 Symbol.toStringTag 标记,然后通过 webpack_require.d 借助 Object.defineProperty 将模块注册到 webpack_exports 上。
总结
这是我对 Webpack 构建出来的文件产物分析,作为自己学习过程中的笔记记录下来,后续可能在学习中继续补充新的理解以及修改其中存在的错误。