node 有 3中类型的模块:
1、我们平常使用的普通模块,
2、node native 的js 模块,
3、node native 的c++ 模块,
这一个章主要就是通过源码分析这3中模块加载的细节。
1.普通模块的加载
文件:lib\module.js
普通模块加载用是lib\module.js中 Module.prototype.require
进行加载的
==> Module._load
==> module.load 加载的步骤是先检查cache,命中返回,再检查是否是native js 模块,命中返回,最后才执行加载
load的时候会根据文件名后缀调用相应的加载函数,
var extension = path.extname(filename) || '.js';
if (!Module._extensions[extension]) extension = '.js';
Module._extensions[extension](this, filename);
我们这里以js为例
==> Module.prototype._compile 编译执行我们js内容
调用包裹的匿名函数,执行js 这个 args 对应 匿名函数的参数列表
(function (exports, require, module, __filename, __dirname) {})
大家注意这里的 require 是由一个函数调用返回的
var require = internalModule.makeRequireFunction.call(this);
internalModule
在 lib\internal\module.js 文件中定义。但是它最终调用的还是 Module.prototype.require
,只是包裹了一下
所以呢,平时使用的 require
就是这里传入的这个require
了。
2. node native 的js 模块加载
文件: lib\internal\bootstrap_node.js
node native 的js 模块使用NativeModule.require
进行加载的,NativeModule.require
定义在 lib\internal\bootstrap_node.js (也就是node启动过程中的node.js,实际是bootstrap_node.js ) 文件中。
native js加载的时候也是先检查cache,没有命中才去加载native js。但是到哪里去加载nativa js呢,看下图中有一个变量 NativeModule._source
,它存储着
native js的所有模块信息,但是你又发现,它其实是通过 process.binding('natives')
加载的,而process.binding
是加载native c++ 模块的(待会下面会介绍),所以说呢,node把native js 所有模块当做一个名字是(natives)的native c++ 模块进行加载的
==> NativeModule.prototype.compile
接下来就是编译执行,但是在调用的时候参数都基本和普通模块的差不多,但是有一个区别,就是 require 对应的部分,在这个是传入的 NativeModule.require
,所以呢,我们平时用的 require 和 native js模块里面的 require 是不一样的,分别对应 Module.prototype.require
和 NativeModule.require
3.node native 的c++ 模块加载
文件: node.cc
node native 的c++ 模块是通过process.binding
进行加载的,process 是 传入 bootstrap_node.js 的一个c++对象,上一章讲了 bootstrap_node.js 中会对process 进行部分信息初始化,,其实这只是很少的一部分,,大部分都在 c++ 部分初始化的。
process的初始化过程:
上一章讲node 启动过程中有一个 LoadEnvironment(env) 调用, 在这之前有个
Environment* env = CreateEnvironment(isolate, context, instance_data);
==> SetupProcessObject
进行process的初始化,部分信息如下:
这其中也包括加载c++模块的函数 binding 的初始化,
知道了 binding ,来分析 c++模块的加载,我们可以看出 process.binding 的调用最终会定位到 Binding 函数 加载前也会先检查cache,没有才调用 get_builtin_module 加载相应的模块。 到一个链表中 modlist_builtin 去查找相应的模块。
而所有的 c++模块都是通过一个宏添加到这个链表中的(在编译时),更多信息参考 bigtree9307的文章
#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc)
补充:
native js 的所有模块字节信息加载 process.binding('natives')
加载, 上面说到node把native js 所有模块当做一个名字是(natives)的native c++ 模块进行加载的,如图:
当加载一个名字叫 natives 的c++模块的时候,就调用 DefineJavaScript
注意到DefineJavaScript 是遍历一个变量,其实 natives 是一个数组,定义在 node_natives.h(工具生成的头文件)中,里面存储着native js 字节数组配置信息,
里面的 internal_bootstrap_node_native,_debug_agent_native,assert_native…等等都是字节数字,存储着相应的native js 文件信息。
到这里基本上对3种模块的加载基本走了一遍,,有错的地方 ,希望大家指教。
里面的 internal_bootstrap_node_native,_debug_agent_native,assert_native…等等都是字节数字,存储着相应的native js 文件信息。
这里面的内容是从哪里加载的