目前在使用 webpack 开发的时候,经常会出现 CommonJs 模块和 ESModule 模块混用的情况。由于这两种的模块是基于不同的规范定义的,那么它们之间是如何相互加载的呢?
我们来看一下以下几种情况:
1. CommentJs 加载 CommentJs
- 定义一个
CommonJs模块
// module.js
module.exports = {
name: "CommonJs module",
desc: "module description"
}
- 使用
CommentJs的方法require加载该模块
// index.js
const module = require("./module.js")
console.log('module.name :>> ', module.name);
console.log('module.desc :>> ', module.desc);
- webpack 打包结果
(() => {
// module.js 模块代码
var webpackModules = {
"./src/module.js": (module, exports, require) => {
module.exports = {
name: "CommonJs module",
desc: "module description"
};
}
};
// webpack 定义的模块加载的方法
var webpackModuleCache = {};
function webpackRequire(moduleId) {
// 判断该模块是否加载过,若加载过直接取缓存
var cachedModule = webpackModuleCache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// 定义一个空的 module 对象,用于存储当前加载模块的导出 `exports`
var module = webpackModuleCache[moduleId] = {
exports: {}
};
// 执行要加载的模块代码
webpackModules[moduleId](module, module.exports, webpackRequire);
// 返回模块导出
return module.exports;
}
var webpackExports = {};
// 入口文件 index.js
(() => {
// 通过 webpackRequire 加载模块
const module = webpackRequire("./src/module.js");
console.log('module.name :>> ', module.name);
console.log('module.desc :>> ', module.desc);
})();
})();
- 模块文件
- 首先
CommonJs的模块会被包装成一个函数,并传入module, exports, require参数。模块的源代码没有改变。以模块路径作为moduleId存储在webpackModules对象中。 - webpack 会定义一个
webpackRequire方法用来加载模块。- 判断该模块是否加载过,若加载过直接取缓存
- 定义一个空的 module 对象,用于存储当前加载模块的导出
exports - 执行要加载的模块代码
- 返回模块导出
- 首先
- 入口文件
- 通过
webpackRequire方法来加载。
- 通过
2. CommentJs 加载 ESModule
- 定义一个ESModule
// module.js
export default 'ESModule default';
export const temp = "Templates"
- 使用
CommentJs的方法require加载该ESModule模块。
const module = require("./module.js")
console.log('module :>> ', module);
console.log('module.dafault :>> ', module.default);
console.log('module.temp :>> ', module.temp);
- webpack打包后的代码
(() => {
var webpackModules = {
"./src/module.js": (unusedWebpackModule, webpackExports, webpackRequire) => {
"use strict";
// 定义该模块为 ESModule
webpackRequire.r(webpackExports);
// 重写ESModule的导出对象
webpackRequire.d(webpackExports, {
"default": () => webpackDefaultExport,
"temp": () => temp
});
const webpackDefaultExport = 'ESModule';
const temp = "Templates";
}
};
// webpackRequire
var webpackModuleCache = {};
function webpackRequire(moduleId) {
var cachedModule = webpackModuleCache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = webpackModuleCache[moduleId] = {
exports: {}
};
webpackModules[moduleId](module, module.exports, webpackRequire);
return module.exports;
}
(() => {
// 定义对象属性的getter
webpackRequire.d = (exports, definition) => {
for (var key in definition) {
if (webpackRequire.o(definition, key) && !webpackRequire.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
}
}
};
})();
(() => {
webpackRequire.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();
(() => {
// 定义该模块为一个ESModule的标志
webpackRequire.r = exports => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module'
});
}
Object.defineProperty(exports, 'esmodule', {
value: true
});
};
})();
var webpackExports = {};
(() => {
const module = webpackRequire("./src/module.js");
console.log('module :>> ', module);
console.log('module.default :>> ', module.default);
console.log('module.desc :>> ', module.temp);
})();
})();
- 模块文件
webpack会对ESModule进行一个简单的修改.- 首先也是同样将模块包装成了一个箭头函数,并传入
module, exports, require参数。 - 通过
webpackRequire.r标志为一个ESModule模块。 - 通过
webpackRequire.d将要导出的属性设置一个getter方法。这样也保证了导出的是值的引用 - 重写了导出的 默认导出 以及 变量导出。
- 入口文件
- 通过
webpackRequire方法来加载。 - 通过
module.default来获取默认导出
- 通过
3. ES 加载 ES
export default "name"
export const age = 27;
import name, {age} from "./module"
console.log('name :>> ', name);
console.log('age :>> ', age);
(() => {
"use strict";
var webpackModules = {
"./src/module.js": (unusedWebpackModule, webpackExports, webpackRequire) => {
webpackRequire.r(webpackExports);
webpackRequire.d(webpackExports, {
"age": () => age,
"default": () => webpackDefaultExport
});
const webpackDefaultExport = "name";
const age = 27;
}
};
var webpackModuleCache = {};
function webpackRequire(moduleId) {
var cachedModule = webpackModuleCache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = webpackModuleCache[moduleId] = {
exports: {}
};
webpackModules[moduleId](module, module.exports, webpackRequire);
return module.exports;
}
(() => {
webpackRequire.d = (exports, definition) => {
for (var key in definition) {
if (webpackRequire.o(definition, key) && !webpackRequire.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
}
}
};
})();
(() => {
webpackRequire.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();
(() => {
webpackRequire.r = exports => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module'
});
}
Object.defineProperty(exports, 'esmodule', {
value: true
});
};
})();
var webpackExports = {};
(() => {
webpackRequire.r(webpackExports);
var _modulewebpackImportedModule0 = webpackRequire("./src/module.js");
console.log('name :>> ', _modulewebpackImportedModule0["default"]);
console.log('age :>> ', _modulewebpackImportedModule0.age);
})();
})();
- 模块文件
ESModule的模块还是通过同样的方式进行封装
- 入口文件
- 首先定义一个空对象
{},并将该对象标记为一个ESModule模块。 - 通过
webpackRequire方法加载模块代码 - 通过
module.default来获取默认导出
- 首先定义一个空对象
4. ES 加载 CommentJs
- 定义CommentJs模块
// module.js
module.exports = {
name: "common_module",
desc: "A common module"
}
- 通过ESModule的引入方式引入
import module from "./module"
console.log('name :>> ', module.name);
console.log('desc :>> ', module.desc);
- 导出结果
(() => {
var webpackModules = {
"./src/module.js": module => {
module.exports = {
name: "common_module",
desc: "A common module"
};
}
};
var webpackModuleCache = {};
function webpackRequire(moduleId) {
var cachedModule = webpackModuleCache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = webpackModuleCache[moduleId] = {
exports: {}
};
webpackModules[moduleId](module, module.exports, webpackRequire);
return module.exports;
}
(() => {
webpackRequire.n = module => {
var getter = module && module.esmodule ? () => module['default'] : () => module;
webpackRequire.d(getter, {
a: getter
});
return getter;
};
})();
(() => {
webpackRequire.d = (exports, definition) => {
for (var key in definition) {
if (webpackRequire.o(definition, key) && !webpackRequire.o(exports, key)) {
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key]
});
}
}
};
})();
(() => {
webpackRequire.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();
(() => {
webpackRequire.r = exports => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module'
});
}
Object.defineProperty(exports, 'esmodule', {
value: true
});
};
})();
var webpackExports = {};
(() => {
"use strict";
webpackRequire.r(webpackExports);
var _modulewebpackImportedModule0 = webpackRequire("./src/module.js");
var _modulewebpackImportedModule0_Default = webpackRequire.n(_modulewebpackImportedModule0);
console.log('name :>> ', _modulewebpackImportedModule0_Default().name);
console.log('desc :>> ', _modulewebpackImportedModule0_Default().desc);
})();
})();