Skip to content

AHABHGK

模块机制

引入模块需要三步:

  1. 路径分析
  2. 文件定位
  3. 编译执行

模块分为核心模块和文件模块

  • 核心模块:Node 提供,与 Node 源代码一起编译进二进制文件,Node 进程启动时直接加载到内存中,require 时省略文件定位和编译执行,而且路径分析时会优先判断,所以加载速度最快
  • 文件模块:用户编写的模块,运行时动态加载

模块引入时优先从缓存中引入,缓存中没有就用 fs 模块同步读取引入并加到缓存中

1// 每一个模块都是一个对象
2function Module(id, parent) {
3 this.id = id;
4 this.exports = {};
5 this.parent = parent;
6 if (parent && parent.children) {
7 parent.children.push(this);
8 }
9 this.filename = null;
10 this.loaded = false;
11 this.children = [];
12}

用户模块

1// > console.log(module)
2// Module {
3// id: '<repl>',
4// path: '.',
5// exports: {},
6// parent: undefined,
7// filename: null,
8// loaded: false,
9// children: [],
10// paths: [
11// ...
12// '/Users/ahabhgk/node_modules',
13// '/Users/node_modules',
14// '/node_modules',
15// ...
16// ]
17// }
18
19// > console.log(require)
20// [Function: require] {
21// resolve: [Function: resolve] { paths: [Function: paths] },
22// main: undefined,
23// extensions: [Object: null prototype] {
24// '.js': [Function],
25// '.json': [Function],
26// '.node': [Function],
27// '.mjs': [Function]
28// },
29// cache: [Object: null prototype] {}
30// }
31
32// Native extension for .json
33Module._extensions['.json'] = function(module, filename) {
34 var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
35 try {
36 module.exports = JSON.parse(stripBOM(content));
37 } catch (err) {
38 err.message = filename + ': ' + err.message; throw err;
39 }
40};
41
42// Native extension for .js
43Module._extensions['.js'] = function(module, filename) {
44 var content = NativeModule.require('fs').readFileSync(filename, 'utf8');
45 try {
46 content = '(function (exports, require, module, __filename, __dirname) {' + content + '});'
47 var fn = vm.runInThisContext(content) // 类似 eval,只是具有明确上下文,不会污染全局
48 fn(exports, require, module, filename, dirname)
49 } catch (err) {
50 err.message = filename + ': ' + err.message; throw err;
51 }
52};
53
54//Native extension for .node (C/C++ 扩展模块)
55Module._extensions['.node'] = process.dlopen;

C/C++ 扩展模块通过 node-gyp 编译成 .node 文件,.node 在 *nix 平台就是 .so 文件,Windows 平台就是 .dll 文件,通过 libuv 实现跨平台

require() 引入 .node 文件的过程

process.dlopen('./hello.node', exports) 把扩展模块返回结果放到 exports 空对象中

核心模块

由 C/C++ 编写的称为 buildin module 内建模块(比如 node_os、node_fs、node_buffer、node_crypto、node_http_parser、node_zlib),会放在 node_module_list 数组中,加载内建模块时会先建一个 exports 空对象,从 node_module_list 取出内建模块填充 exports

核心模块 .js 文件编译时会先把 js 代码转换为 C/C++ 代码,引入时通过 process.binding() 取出 '(function (exports, require, module, __filename, __dirname) {' + content + '});' 包裹后的代码并进行缓存

核心模块 .js 文件和内建模块的关系是全部或部分是依赖于内建模块,.js 文件通过 node_module_list 取出内建模块,用户 require 时通过 process.binding 取出

os 核心模块引入流程