我们先写个简单demo,然后webpack build后观察产出bundle方式来了解webpack是怎么实现的js模块化的
测试环境webpack5
写个简单demo
//index.js
import * as d from "./demo/demo";
import * as d2 from "./demo/demo2";
console.log(d.a);
console.log(d2.a);
//demo.js
import * as d from "./demo2";
export let a = d;
//demo2.js
export let a = { a: 1 };
webpack build
webpack build生成了bundle.js截了部分代码如下
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./src/demo/demo.ts":
/*!**************************!*\
!*** ./src/demo/demo.ts ***!
\**************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "a": () => (/* binding */ a)
/* harmony export */ });
/* harmony import */ var _demo2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo2 */ "./src/demo/demo2.ts");
let a = _demo2__WEBPACK_IMPORTED_MODULE_0__;
/* */
/***/ }),
/***/ "./src/demo/demo2.ts":
/*!***************************!*\
!*** ./src/demo/demo2.ts ***!
\***************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "a": () => (/* binding */ a)
/* harmony export */ });
let a = { a: 1 };
...
注释太多影响可读性,写个正则表达式去掉注释,正则表达式如下
/.*?\*/|//.*|/\*(.|\r\n|\n)*?\*/
继续我们格式化代码,现在代码如下
(() => {
"use strict";
var __webpack_modules__ = {
"./src/demo/demo.ts": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
a: () => a,
});
var _demo2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
"./src/demo/demo2.ts"
);
let a = _demo2__WEBPACK_IMPORTED_MODULE_0__;
},
"./src/demo/demo2.ts": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
a: () => a,
});
let a = { a: 1 };
},
};
e;
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) => {
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 _demo_demo__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/demo/demo.ts");
var _demo_demo2__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
"./src/demo/demo2.ts"
);
console.log(_demo_demo__WEBPACK_IMPORTED_MODULE_0__.a);
console.log(_demo_demo2__WEBPACK_IMPORTED_MODULE_1__.a);
})();
})();
webpack模块实现讲解
如果分析上面代码,bundle里面代码功能主要分三块功能,分别是
- __webpack_modules__变量
- __webpack_require__函数
- index代码执行
我们按照webpack_require,webpack_modules,index代码执行顺序讲解代码
webpack_require
我们来看__webpack_require__.r(),传了export对象,然后标记为模块
(() => {
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
})();
我们来看__webpack_require__.o(),只是hasOwnProperty的封装
(() => {
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
})();
我们来看__webpack_require__.d(),defintion对象浅拷贝到exports对象,并且设置为只读且可遍历
(() => {
__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__(), 传进来的id缓存过,那么从缓存取exports,没有则从__webpack_modules__取模块并且初始化,然后存到__webpack_modules__
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_modules
__webpack_modules__对象分别存了demo.js跟demo2.js文件,索引为文件名,后面是函数。 这个函数是在模块初始化的时候执行
先看代码逻辑
- 代码逻辑是声明__webpack_exports__为模块
- 拷贝代码里的export的字段到__webpack_exports__
- 执行文件里的代码逻辑
var __webpack_modules__ = {
"./src/demo/demo.ts": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
a: () => a,
});
var _demo2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
"./src/demo/demo2.ts"
);
let a = _demo2__WEBPACK_IMPORTED_MODULE_0__;
},
"./src/demo/demo2.ts": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
a: () => a,
});
let a = { a: 1 };
},
};
来看参数
参数来自于__webpack_require__,可以翻看下面代码
//__webpack_require__
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
index代码执行
我们理解了__webpack_modules__跟__webpack_require__不难看出初始化的代码逻辑。故只列出源代码,跳过讲解
var __webpack_exports__ = {};
(() => {
__webpack_require__.r(__webpack_exports__);
var _demo_demo__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__("./src/demo/demo.ts");
var _demo_demo2__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
"./src/demo/demo2.ts"
);
console.log(_demo_demo__WEBPACK_IMPORTED_MODULE_0__.a);
console.log(_demo_demo2__WEBPACK_IMPORTED_MODULE_1__.a);
})();
总结
webpack实现js模块化使用的原理是通过把各个文件里的代码函数闭包化来实现的。每个js文件闭包化之后,以key-value形式存到__webpack_modules__对象里面通过__webpack_require__读取exports,从而做到避免变量影响全局作用域