从0开始Node.js - common.js规范

1,726 阅读3分钟
  • 开始之前,我们先在本地新建一个目录,然后
npm init -y

生成一个包含package.json的目录结构,然后在目录下新建一个index.js作为入口js文件。新建一个module目录包含index.js及lib.js文件。 之后的目录结构如下:

image.png

  • 首先我们需要知道的是common.js规范的require是会引入js并 执行 下面是个简单的demo: module/index.js文件如下:
console.log('start');
require('./lib');
console.log('end');

module/lib.js文件如下:

console.log('lib');

然后在控制台执行 node module/index.js 结果如下:

image.png

显然lib.js被引入并被执行了。

  • 其次是require函数是有返回值的 我们修改一下上面的代码如下
console.log('start');
const res = require('./lib');
console.log('libres', res);
console.log('end');

再重新执行代码得到的输出结果为:

image.png 可以看到reqiure('./lib.)得到的返回值是一个空对象。 下面问题就来了,如果我希望得到非空的返回对象应该怎么做?

  • exports node.js提供了输出关键字 exports 。 我们如下修改下lib.js代码
console.log('lib');
exports.lib = 'lib'

再次用node执行module目录下面的index.js。

image.png 可以看到require的返回res是一个对象,包含了lib属性。 当然我们还可以exports输出更多的其他类型的变量。

console.log('lib');
exports.lib = 'lib'
exports.obj = { a: 1, b: 2 };
exports.fun = (a) => console.log(a)

控制台打印的结果如下:

image.png

那么exports输出的是对象还是是对象的引用呢?即reqiure得到的对象修改会影响对象的本身吗?测试一下,如下修改module目录下的lib.js和index.js。 index.js

console.log('start');
const res = require('./lib');
res.obj2 = { c: 1, d: 2 }
console.log('end');

lib.js

console.log('lib');
exports.lib = 'lib'
exports.obj = { a: 1, b: 2 };
exports.fun = (a) => console.log(a)

setTimeout(() => { console.log('lib-exports', exports), 1000 })

node module/index.js控制台输出如下:

image.png 可以看到exports和require之间传递的是对象的引用。require对象的修改会影响exports对象。

  • module.exports 如果不想导出的始终是个对象。那么可以使用module.exports导出。 lib.js
console.log('lib');
exports.lib = 'lib'
exports.obj = { a: 1, b: 2 };
exports.fun = (a) => console.log(a);
module.exports = () => { console.log('Node.js') };

setTimeout(() => { console.log('lib-exports', exports), 1000 })

module/index.js

module/index.js
console.log('start');
const res = require('./lib');
console.log('libres', res);
res.obj2 = { c: 1, d: 2 }
console.log('end');

打印结果

image.png 可以看到res打印的结果不再是个object而是一个function。 而同时index.js里面对res添加的属性obj2也不会添加到lib里面的exports对象上。

在浏览器端,webpack打包代码时是支持require命令的。那么看下webpack是如何实现require的。 首先安装webpack和webpack-cli。

npm install webpack webpack-cli -D

此时的package.json文件如下

{
  "name": "nodejs-start-from-0",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {},
  "devDependencies": {
    "webpack": "^5.37.1",
    "webpack-cli": "^4.7.0"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

注意安装的webpack和webpack-cli版本,此版本应该是2021年5月份左右最新的版本 然后执行如下命令:

webpack ./index.js --devtool source-map  --mode development --target node

输出结果在dist目录下的main.js文件中

/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ({

/***/ "./lib.js":
/*!****************!*\
  !*** ./lib.js ***!
  \****************/
/***/ ((module, exports) => {

console.log('lib');
exports.lib = 'lib'
exports.obj = { a: 1, b: 2 };
exports.fun = (a) => console.log(a);
module.exports = () => { console.log('Node.js') };

setTimeout(() => { console.log('lib-exports', exports), 1000 })

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// 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__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!******************!*\
  !*** ./index.js ***!
  \******************/
console.log('start');
const res = __webpack_require__(/*! ./lib */ "./lib.js");
console.log('libres', res);
res.obj2 = { c: 1, d: 2 }
console.log('end');
})();

/******/ })()
;
//# sourceMappingURL=main.js.map

可以看到lib.js被放在了__webpack_modules__对象中,我们写在lib.js里面的代码被放在key为'lib.js'的属性里面。 然后在index.js里面通过执行

const res = __webpack_require__(/*! ./lib */ "./lib.js");

导入的lib.js。 下面看看__webpack_require__的实现

/******/ 	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;
/******/ 	}

从main.js里面拷出来 前面是判断缓存。后面__webpack_modules__[moduleId](module, module.exports, webpack_require);这一句其实就是执行的lib.js。同时传入了module和module.exports。 在lib.js执行的部分可以看到:

module.exports = () => { console.log('Node.js') };

exports最终被module.exports覆盖成了这个。所以输出的是module.exports的这个function。

以上。