本文正在参加「金石计划 . 瓜分6万现金大奖」
你是否会好奇当我们使用 webpack
打包项目时最后生成的文件是什么样子的呢?假如我们有一个页面入口文件 index.js
和依赖模块 utils.js
文件,代码如下:
// 入口文件 index.js
import dateUtils from './utils'
dateUtils.print()
// 模块文件 utils.js
export default {
print() {
console.log('utils.js==>>print', new Date())
}
}
在开发环境下执行 npm run build
生成bundle.js
文件。
首先我们看看bundle.js
的上半部分:
;(() => {
// 这就是我们对应的模块文件的内容,webpack编译index.js、utils.js文件后就放在这个__webpack_modules__变量中
// 里面的代码被eval包裹,eval的作用就是把里面的字符串转化为代码进行执行
var __webpack_modules__ = {
'./src/index.js': (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
eval(
// index.js里面有import,就调用__webpack_require__方法,这个方法返回util模块的exports
var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils.js");
_utils__WEBPACK_IMPORTED_MODULE_0__["default"].print()
)
},
'./src/utils.js': (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
eval(
// 这个方法是往exports上挂载内容
__webpack_require__.d(__webpack_exports__, {
"default": () => (__WEBPACK_DEFAULT_EXPORT__) });
const __WEBPACK_DEFAULT_EXPORT__ = { print() {
console.log(\'DateUtils.js==>>print\', new Date())
}
}
)
}
}
它定义了一个__webpack_modules__
对象,这个对象的 key
是模块文件的相对路径,value
是一个函数:
(
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
eval(
__webpack_require__.r(__webpack_exports__);
// 看到index.js里面有import,那么就调用__webpack_require__方法,这个方法返回utile模式的exports
var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils.js");
_utils__WEBPACK_IMPORTED_MODULE_0__["default"].print()
)
}
这个函数传入三个参数,分别是__unused_webpack_module
, __webpack_exports__
对象,__webpack_require__
方法。这个可以类比nodejs
里面的模块,在nodejs
代码执行的时候,也会给模块加上require,module,exports
三个参数,也就是webpack
实现了commonjs
规范,这样webpack
才会识别require import
这样的语法。
webpack 兼容 esm 和 cjs 模块规范,在webpack项目中我们可以愉快的使用这两种规范。
接下来webpack
实现了一个require
函数,即__webpack_require__
函数。当我们在模块中使用import from
语法时,在编译过程中会把import from
替换为__webpack_require__
,这个方法的实现非常简单:
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属性
exports: {},
});
// 执行模块
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// 返回模块的exports对象
return module.exports;
}
最后就是执行入口文件:
var __webpack_exports__ = __webpack_require__('./src/index.js')
入口文件的函数如下:
// 通过__webpack_require__引用utils里面的内容
var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__('./src/utils.js')
// _utils__WEBPACK_IMPORTED_MODULE_0__对象上有个default属性,调用这个属性上面的值,并执行
_utils__WEBPACK_IMPORTED_MODULE_0__['default'].print()
utils
模块文件的函数如下:
// __webpack_require__.d的作用是在module.exports挂载default属性,这个属性的值为__WEBPACK_DEFAULT_EXPORT__
__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__
})
const __WEBPACK_DEFAULT_EXPORT__ = {
print() {
console.log('DateUtils.js==>>print', new Date())
}
}
上面是开发环境打包之后的文件结构,那么在生产环境打包的文件代码是什么样子的呢?
;(() => {
'use strict'
;({
print() {
console.log('DateUtils.js==>>print', new Date())
}
}.print())
})()
可以看到代码非常简洁,没有了开发环境相关的代码,把模块内的所有代码都压缩在一个文件之中了,这样大大的减少了bundle.js
的体积。