- 参考,
- webpack: 4.35.2 版本
整体结构
代码语言:javascript复制(function webpackUniversalModuleDefinition(root, factory){
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if (typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for (var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(window, function(){
return (function(modules){ // webpackBootstrap
// 模块结果的缓存
var installedModules = {};
// 模块化方法的定义
...
// 执行入口文件
return __webpack_require__(__webpack_require__.s = "./src/app.js");
})(
// 模块文件的映射(文件名: 模块的定义),通过该该map来查找模块定义
{
"./src/app.js": (function(module, __webpack_exports__, __webpack_require__){...}),
"./src/counter.js": (function(module, exports){...})
});
});
模块方法实现
webpack_require
代码语言:javascript复制// moduleId 可以理解为模块名称
function __webpack_require__(moduleId){
// Check if module is in cache
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 创建并缓存模块
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// 执行模块的定义
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 模块加载完成的标识
module.l = true;
// 返回模块导出的结果
return module.exports;
}
webpack_require.d
esModule通过该方法定义模块,重点在getter函数,通过闭包实现了esModule的特性:引用
代码语言:javascript复制// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter){
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {enumerable: true, get: getter});
}
};
webpack_require.r
defineProperty __esModule,标识为es Module
代码语言:javascript复制// define __esModule on exports
__webpack_require__.r = function(exports){
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {value: 'Module'});
}
Object.defineProperty(exports, '__esModule', {value: true});
};
webpack_require.n
代码语言:javascript复制// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module){
var getter = module && module.__esModule ?
function getDefault(){
return module['default'];
} :
function getModuleExports(){
return module;
};
__webpack_require__.d(getter, 'a', getter);
return getter;
};
其它
- hasOwnProperty、
- modules、installedModules、
- __webpack_public_path__
__webpack_require__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.o = function(object, property){
return Object.prototype.hasOwnProperty.call(object, property);
};
// __webpack_public_path__
__webpack_require__.p = "";
webpack_require.t 作用??
代码语言:javascript复制 // create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value;
if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
return ns;
};
示例模块编译结果
commonjs
app.js
代码语言:javascript复制const { num, increase } = require("./b");
console.log(num); // 1
increase();
console.log(num); // 1
代码语言:javascript复制function(module, exports, __webpack_require__) {
const { num, increase } = __webpack_require__(/*! ./b */ "./src/b.js");
console.log(num);
increase();
console.log(num);
}
counter.js
代码语言:javascript复制let num = 1;
function increase() {
return num ;
}
module.exports = { num, increase };
代码语言:javascript复制function(module, exports) {
let num = 1;
function increase() {
return num ;
}
module.exports = { num, increase };
}
小结
num 属于基本类型,假设其内存地址指向 n1,当它被 赋值 给 module.exports['num'] 时,module.exports['num'] 已经指向了一个新的内存地址 n2,只不过其值同样为 1,但和 num 已是形同陌路,毫不相干。
代码语言:javascript复制let num = 1;
// mun 相当于 module.exports['num']
mun = num;
代码语言:javascript复制num = 999;
console.log(mun); // 1
increase 是一个函数,属于引用类型,即 increase 只作为一个指针,当它被赋值给 module.exports['increase'] 时,只进行了指针的复制,是 浅拷贝,其内存地址依旧指向同一块数据。所以本质上 module.exports['increase'] 就是 increase,只不过换个名字。 而由于词法作用域的特性,counter.js 中 increase() 修改的 num 变量在函数声明时就已经绑定不变了,永远绑定内存地址指向 n1 的 num.
es Module
app.js
代码语言:javascript复制import { num, increase } from "./counter";
console.log(num); // 1
increase();
console.log(num); // 2
代码语言:javascript复制function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _counter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./counter */ "./src/counter.js");
console.log(_counter__WEBPACK_IMPORTED_MODULE_0__["num"]);
Object(_counter__WEBPACK_IMPORTED_MODULE_0__["increase"])();
console.log(_counter__WEBPACK_IMPORTED_MODULE_0__["num"]);
}
counter.js
代码语言:javascript复制let num = 1;
function increase() {
return num ;
}
export { num, increase };
代码语言:javascript复制function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "num", function() { return num; });
__webpack_require__.d(__webpack_exports__, "increase", function() { return increase; });
let num = 1;
function increase() {
return num ;
}
}
小结
与 CommonJS 不同,ES Modules 并没有对 module.exports 直接赋值,而是将值作为函数的返回值,再把函数赋值给 module.exports,之前我们提过词法作用域的概念,即这里的 num() 和 increase() 无论在哪里执行,返回的 num 变量和 increase 函数都是 counter.js 中的。
总结
问: CommonJS 和 ES Modules 中模块引入的区别?
CommonJS 输出的是一个值的拷贝;ES Modules 生成一个引用,等到真的需要用到时,再到模块里面去取值,模块里面的变量,绑定其所在的模块。