0%

2022-04-13-Nodejs模块加载研究

1. 这个功能是什么#

NodeJS 中有两种模块加载方式:CommonJS,ECMAScript modules;前者为 NodeJS 内部实现,ES Modules 为 JS 标准加载方式。

1.1. CommonJS#

在 Node.js 模块系统中,每个文件都被视为一个单独的模块。

1.2. ECMAScript modules#

ECMAScript 模块是打包 JavaScript 代码以供重用的官方标准格式。使用 import 和 export 导入导出模块。
Node.js 默认将 JavaScript 代码视为 CommonJS 模块。可以通过.mjs 文件扩展名,package.json ‘type’字段,将 JavaScript 代码将 ECMAScript 视为模块

2. 这个功能如何实现#

2.1. CommonJS#

2.1.1. 模块包装器#

Nodejs 中的模块是可执行的,nodejs 使用类似以下语法将模块封装为函数

1
2
3
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});

通过这种方式

  1. 它将顶级变量(用 var、const 或 let 定义)的作用域限定在模块上,而不是全局对象上。
  2. 通过将模块内的数据导出到对象(exports,module),供外部调用者使用,同时引入了在模块内可使用的变量(__filename, __dirname)

3. 这个功能相关的参与者与接口#

3.1. CommonJS 接口#

3.1.1. 3.2.1.#

3.1.2. __dirname#

文件绝对路径所在的目录

3.1.3. __filename#

文件的绝对路径

3.1.4. exports module.exports#

exports 是 module.exports 的简写,两者关系如下相关资料

  • exports 只能使用语法来向外暴露内部变量:如http://exports.xxx = xxx;
  • module.exports 既可以通过语法,也可以直接赋值一个对象。

伪代码实现说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}

建议不要使用 exports,因为 module.exports 都能做,还不会出错

3.2. ES modules#

3.2.1. specifier (区分符)#

区分符 specifier 指以下文句中的 ‘path’

1
import { sep } from 'path'

specifier 有如下形式

  1. 相对路径。它们引用相对于导入文件位置的路径。对于这些文件,文件扩展名总是必需的。如 ‘./startup.js’ or ‘../config.mjs’
  2. 包名。它们可以通过包名引用包的主入口点,也可以根据示例分别引用以包名作为前缀的包内的特定特性模块。
  3. 绝对路径。直接和显式地引用了一个完整的路径。’file:///opt/nodejs/config.js’.

4. CommonJS 与 ECMAScript modules 互操作#

4.1. import#

import 语句(import * from ‘xxx’)只允许在 ES 模块中使用,但在 CommonJS 中支持动态 import()表达式来加载 ES 模块。

4.2. require#

只用来加载 CommonJS 模块

4.3. CommonJS Namespaces#

CommonJS 模块由一个 module.exports 对象组成。可以导出任何类型的对象

5. 相关资料#

  1. CommonJS
    http://nodejs.cn/api/modules.html
  2. ECMAScript modules
    http://nodejs.cn/api/esm.html

6. 示例#

6.1. commonjs#

6.1.1. 使用 module.exports 导出#

  • 库文件
1
2
3
4
5
6
function add(a, b) {
return a + b;
}

module.exports = add;

  • 主函数文件
1
2
let add_commonjs_modules_exports = require('./modules/add_commonjs_modules_exports.js');
console.log(add_commonjs_modules_exports(1, 2));

6.1.2. 使用 exports 导出#

  1. 给 exports 的成员赋值
  • 库文件
1
2
3
4
5
6
function add(a, b) {
return a + b;
}

exports.add = add;

  • 主函数文件
1
2
let add_commonjs_exports = require('./modules/add_commonjs_exports.js');
console.log(add_commonjs_exports.add(1, 2));
  1. 给 exports 赋值(此方法无法导出)
  • 库文件
1
2
3
4
5
6
function add(a, b) {
return a + b;
}

exports = add; //此种方式无法导出任何内容

  • 主函数文件
1
2
let add_commonjs_exports_error = require('./modules/add_commonjs_exports_error.js');
console.log(add_commonjs_exports_error(1, 2)); //此种有错误,模块内容有误

6.2. ESModules#

6.2.1. 使用 export 导出,注意其中 default 使用#

  • 库文件
1
2
3
4
5
6
export function add(a, b) {
return a + b;
}

export default add; //用于 import XXX from ""这种简写形式

  • 主函数文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import * as fun from './modules/add_ES_export.mjs';
console.log(fun);
console.log(fun.add(1, 2));
/*
fun.add = function (a, b) { //由export导出的为const
return a - b;
};*/
fun.default.add = function (a, b) {
//由export导出的为const,但其对象属性可修改
return a - b;
};
console.log(fun.add(1, 2));
console.log(fun.default.add(1, 2));

import { add } from './modules/add_ES_export.mjs';
console.log(add(1, 2));

import a from './modules/add_ES_export.mjs'; //等价于 import { default as a } from './modules/add_ES_export.mjs';
console.log(a);

import { default as b } from './modules/add_ES_export.mjs';
console.log(b);


6.3. 用 import 加载 commonjs 模块#

  • 库文件
1
2
3
4
5
function add(a, b) {
return a + b;
}

module.exports = add;
  • 主程序文件
1
2
3
4
5
import * as c from './modules/add_commonjs_modules_exports.js'; //es中调用commonjs模块
console.log(c.default(1,2));

import add1 from './modules/add_commonjs_modules_exports.js';
console.log(add1(1,2));