CommonJs模塊化實現原理
首先看一個案例
初始化項目
npm init
npm i webpack -D
目錄結構如下:
webpack.config.js
const path = require("path");
module.exports = {mode: "development",entry: "./src/index.js",output: {path: path.resolve(__dirname, "dist"),filename: "[name].js",},devtool: "source-map"
};
src/index.js
const b = require("./b");
const a = require("./a");
console.log(a, b)
src/a.js
let a = "這是a"
module.exports = a;
src/b.js
let b = "這是b"
module.exports = b;
build.js
const { webpack } = require("webpack");
const webpackOptions = require("./webpack.config.js");const compiler = webpack(webpackOptions);compiler.run((err, stats) => {console.log(err)
});
進行node ./build.js后查看dist文件下
(() => { var __webpack_modules__ = ({"./src/a.js": ((module) => {let a = "這是a"module.exports = a;}),"./src/b.js":((module) => {let b = '這是b'module.exports = b;})});// The module cachevar __webpack_module_cache__ = {};// The require functionfunction __webpack_require__(moduleId) {// Check if module is in cachevar cachedModule = __webpack_module_cache__[moduleId];if (cachedModule !== undefined) {return cachedModule.exports;}// Create a new module (and put it into the cache)var module = __webpack_module_cache__[moduleId] = {// no module.id needed// no module.loaded neededexports: {}};// Execute the module function__webpack_modules__[moduleId](module, module.exports, __webpack_require__);// Return the exports of the modulereturn module.exports;}var __webpack_exports__ = {};(() => {const b = __webpack_require__("./src/b.js");const a = __webpack_require__("./src/a.js");console.log(a, b);})();})()
;
//# sourceMappingURL=main.js.map
分析一下打包產物
首先看下 webpack_modules,我們在src/index.js中引入了a.js、b.js, webpack會把’src/a.js’、‘src/b.js’作為modules的key值,該模塊內容作為modules的value值;
var __webpack_modules__ = ({"./src/a.js": ((module) => {let a = "這是a"module.exports = a;}),"./src/b.js":((module) => {let b = '這是b'module.exports = b;})});
定義 __webpack_require__函數
var __webpack_module_cache__ = {};function __webpack_require__(moduleId) {// 判斷一下緩存中有沒有當前modulevar cachedModule = __webpack_module_cache__[moduleId];if (cachedModule !== undefined) {// 緩存中有的話走緩存,返回cachedModule.exportsreturn cachedModule.exports;}// 緩存中沒有就重新創建一個moudle,設置export對象,并放入緩存var module = __webpack_module_cache__[moduleId] = {exports: {}};// 執行模塊代碼, 傳入當前module,根據需要傳入module.exports, __webpack_require__,module.exports會在模塊中賦值__webpack_modules__[moduleId](module, module.exports, __webpack_require__);// 返回module.exportsreturn module.exports;}
執行入口函數,為防止命名污染,封裝成立即執行函數。
(() => {const b = __webpack_require__("./src/b.js");const a = __webpack_require__("./src/a.js");console.log(a, b);})();
ES Module模塊化原理
src/index.js
import a from './a'
import {b} from './b'
console.log(a, b);
src/a.js
const a = "這是a"
export default a
src/b.js
export const b = '這是b'
node ./build.js 之后main.js如下:
(() => { "use strict";var __webpack_modules__ = ({"./src/a.js":((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, {"default": () => (__WEBPACK_DEFAULT_EXPORT__)});const a = "這是a"const __WEBPACK_DEFAULT_EXPORT__ = (a);}),"./src/b.js":((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, {b: () => (b)});const b = '這是b'})});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 _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/a.js");var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/b.js");console.log(_a__WEBPACK_IMPORTED_MODULE_0__["default"], _b__WEBPACK_IMPORTED_MODULE_1__.b);})();})()
;
//# sourceMappingURL=main.js.map
如果是通過export default 方式導出的,那就在 exports 對象加一個 default 屬性
var __webpack_modules__ = ({"./src/a.js":((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {// 在 ESM 模式下聲明 ESM 模塊標識__webpack_require__.r(__webpack_exports__);// 將模塊導出的內容附加的模塊對象上,如果是通過export default導出,給exports的對象加default屬性__webpack_require__.d(__webpack_exports__, {"default": () => (__WEBPACK_DEFAULT_EXPORT__)});const a = "這是a"const __WEBPACK_DEFAULT_EXPORT__ = (a);}),"./src/b.js":((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, {b: () => (b)});const b = '這是b'})});
通過__webpack_require__.r把模塊標識為 ES Module
了解Symbol.toStringTag
(() => {__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__.d對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))})();
ES Module加載CommonJS實現原理
src/index.js
import b from './b'
console.log(b);
src/b.js
let b = '這是b'
module.exports = b;
node ./build.js之后
(() => { var __webpack_modules__ = ({"./src/b.js": ((module) => {let b = '這是b'module.exports = b;})});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__ = {};(() => {"use strict";__webpack_require__.r(__webpack_exports__);var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/b.js");var _b__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_b__WEBPACK_IMPORTED_MODULE_0__);console.log((_b__WEBPACK_IMPORTED_MODULE_0___default()));})();})()
;
//# sourceMappingURL=main.js.map
由此可以看出加了這一個步驟:
通過__webpack_require__.n判斷模塊是否是esModule返回module
(() => {__webpack_require__.n = (module) => {var getter = module && module.__esModule ?() => (module['default']) :() => (module);__webpack_require__.d(getter, { a: getter });return getter;};})();
CommonJS加載ES Module的實現原理
src/index.js
const b = require('./b.js')
console.log(b);
src/b.js
let b = '這是b'
export default b;
node ./build.js之后
(() => {var __webpack_modules__ = ({"./src/b.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {"use strict";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, {"default": () => (__WEBPACK_DEFAULT_EXPORT__)});let b = '這是b'const __WEBPACK_DEFAULT_EXPORT__ = (b);})});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__ = {};(() => {const b = __webpack_require__(/*! ./b.js */ "./src/b.js")console.log(b);})();})()
;
//# sourceMappingURL=main.js.map