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 cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var 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 needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// Return the exports of the module
return 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) {
// 判断一下缓存中有没有当前module
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
// 缓存中有的话走缓存,返回cachedModule.exports
return 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.exports
return 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))
})();