我们把沿用之前的例子,但是在那基础之上把导入的文件改成 ESModule。
/** ./src/index.js **/
let title = require('./title');
console.log(title.default);
console.log(title.age);
/** ./src/title.js **/
export default 'title_name';
export const age = 'title_age';
现在我们来尝试实现以下打包后的源码。
参考 打包后文件分析 我们拷贝过来一些主要通用的代码。
;(()=>{
var modules = {
'./src/title.js':(module, exports, require)=>{
//... 导入的模块代码
}
}
// 缓存
var cache = {};
// require 方法
function require(moduleId){
if(cache[moduleId]){
return cache[moduleId].exports
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
})()
声明模块定义
在模块中,一旦 webpack 检测到你的代码里有 export 和 import 关键字,它就认为这个事 ES Module,然后调用require上的 r 方法 和 d 方法,如下:
;(()=>{
var modules = {
'./src/title.js':(module, exports, require)=>{
// 一旦 webpack 检测到你的代码里有 export 和 import 关键字,它就认为这个事 ES Module
require.r(exports);
require.d(exports, {
default: () => DEFAULT_EXPORT,
age: () => age
});
const DEFAULT_EXPORT = 'title_name';
const age = 'title_age';
}
}
var cache = {};
function require(moduleId){
//如果缓存中有此模块对应的缓存数据
if(cache[moduleId]){
//直接返回模块的结果
return cache[moduleId].exports
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
})()
require.r 方法
这个方法接收导出对象为参数,主要有两个功能
- 给导出对象设置 Symbol.toStringTag 为 Module,通过 Object 原型上的 toString 方法可以获取他的类型字符串 [object Module]
给导出对象新增一个 __esModule 属性为true,表明他是一个 ES Module。 ```javascript ;(()=>{ var modules = { ‘./src/title.js’:(module, exports, require)=>{
// 一旦 webpack 检测到你的代码里有 export 和 import 关键字,它就认为这个事 ES Module
require.r(exports);
require.d(exports, {
default: () => DEFAULT_EXPORT,
age: () => age
});
const DEFAULT_EXPORT = 'title_name';
const age = 'title_age';
} }
var cache = {}; function require(moduleId){ //如果缓存中有此模块对应的缓存数据 if(cache[moduleId]){
//直接返回模块的结果 return cache[moduleId].exports
} var module = cache[moduleId] = {
exports: {}
}; modulesmoduleId; return module.exports; }
// exports.esModule = true 你可以通过它来判断转化前是不是 ES 模块。 require.r = (exports) => { Object.defineProperty(exports, Symbol.toStringTag, { value: ‘Module’ }); Object.defineProperty(exports, ‘esModule’, { value: true }) }
})()
<a name="Syj4y"></a>
## require.d 方法
这个方法接收两个入参
- exports 导出对象
- definition 模块导出的属性
主要的功能就是将被导入模块中导出的属性赋值给 exports。<br />ES 模块默认导出 export default 会挂载到 exports.default 上,age 会挂载到 exports.age 上<br />如下:
```javascript
;(()=>{
var modules = {
'./src/title.js':(module, exports, require)=>{
// 一旦 webpack 检测到你的代码里有 export 和 import 关键字,它就认为这个事 ES Module
require.r(exports);
// ES 模块默认导出 export default 会挂载到 exports.default 上,age 会挂载到 exports.age 上
require.d(exports, {
default: () => DEFAULT_EXPORT,
age: () => age
});
const DEFAULT_EXPORT = 'title_name';
const age = 'title_age';
}
}
var cache = {};
function require(moduleId){
//如果缓存中有此模块对应的缓存数据
if(cache[moduleId]){
//直接返回模块的结果
return cache[moduleId].exports
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
// exports.__esModule = true 你可以通过它来判断转化前是不是 ES 模块。
require.r = (exports) => {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
Object.defineProperty(exports, '__esModule', { value: true })
}
require.d = (exports, definition) => {
//这实际上是一个闭包。
for(var key in definition){
Object.defineProperty(exports, key, { get: definition[key] })
}
}
let title = require('./src/title.js');
console.log(title.default);
console.log(title.age);
})()
结果
webpack 打包源码
基本上和我们自己实现的差不多。但是在源码中还有个 o 方法,这个方法是判断属性是否是对象本身的,而不是通过原型链获取的,对结果影响不是很大。
(() => {
var __webpack_modules__ = ({
"./src/title.js":
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
"age": () => (age),
"default": () => (__WEBPACK_DEFAULT_EXPORT__)
});
const __WEBPACK_DEFAULT_EXPORT__ = ('title_name');
const age = 'title_age';
})
});
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__ = {};
(() => {
let title = __webpack_require__("./src/title.js");
console.log(title);
console.log(title.age);
})();
})()
;