CommonJs模块化实现原理&ES Module模块化原理

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))
 })();

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
相关推荐
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui7 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui
尝尝你的优乐美7 小时前
vue3.0中h函数的简单使用
前端·javascript·vue.js
windy1a7 小时前
【C语言】js写一个冒泡顺序
javascript
会发光的猪。7 小时前
如何使用脚手架创建一个若依框架vue3+setup+js+vite的项目详细教程
前端·javascript·vue.js·前端框架