本文转载自微信公众号《编程杂技》,作者theanarkh。转载本文请联系编程杂技公众号。前言:模块机制是Node.js中非常重要的一个组成部分。模块机制允许我们以模块化的方式编写代码,而不是将所有代码都写到一个文件中。我们平时使用require加载模块的次数比较多,但是对于require的实现原理可能不是很清楚。另外,Node.js中的模块类型很多,加载原理也不一样。本文将介绍Node.js的模块机制和实现原理。1模块机制的初始化和使用1.1注册C++模块Node.js启动时,会通过RegisterBuiltinModules注册C++模块。voidRegisterBuiltinModules(){#defineV(modname)_register_##modname();NODE_BUILTIN_MODULES(V)#undefV}NODE_BUILTIN_MODULES为C语言宏,宏展开如下(类似逻辑略)voidRegisterBuiltinModules(){#defineV(modname)_register_##modname();V(tcp_wrap)V(timers)...其他模块#undefV}展开如下在Node.js源码中找不到这些函数,因为这些函数是在每个C++模块定义的文件(.cc文件的最后一行)中通过宏定义的。以tcp_wrap模块为例,看看它是如何实现的。文件tcp_wrap.cc的最后一句代码NODE_MODULE_CONTEXT_AWARE_INTERNAL(tcp_wrap,node::TCPWrap::Initialize)宏展开是#defineNODE_MODULE_CONTEXT_AWARE_INTERNAL(modname,regfunc)\NODE_MODULE_CONTEXT_AWARE_CPP(modname,regfunc,nullptr,NM_F_INTERNAL)继续展开defineNODE_MODULE_CONTEXT_AWARE_CPP(modname,regfunc,priv,flags)\staticnode::node_module_module={\NODE_MODULE_VERSION,\flags,\nullptr,\__FILE__,\nullptr,\(node::addon_context_register_func)(regfunc),\NODE_STRINGIFY(modname),\priv,\nullptr};\void_register_tcp_wrap(){node_module_register(&_module);}我们看到每个C++模块的底层都定义了一个以_register开头的函数。当Node.js启动时,这些函数将被一个一个地执行。让我们继续看看这些函数的作用。在此之前,我们需要了解Node.js中表示C++模块的数据结构。structnode_module{intnm_version;unsignedintnm_flags;void*nm_dso_handle;constchar*nm_filename;node::addon_register_funcnm_register_func;node::addon_context_register_funcnm_context_register_func;constchar*nm_modname;void*nm_priv;structnode_module*nm_link;};我们看到_register开头的函数调了node_module_register,并传入一个node_module数据结构,那么我们看一下node_module_register的实现;modlist_internal=mp;}elseif(!node_is_initialized){mp->nm_flags=NM_F_LINKED;mp->nm_link=modlist_linked;modlist_linked=mp;}else{thread_local_modpending=mp;}}C++内置模块的flag为NM_F_INTERNAL,所以第一个if逻辑,modlist_internal类似于一个头指针。if里面的逻辑就是通过head插值的方式创建一个单向链表。1.2初始化模块加载器注册C++模块后,初始化模块加载器。MaybeLocalEnvironment::BootstrapInternalLoaders(){EscapableHandleScopescope(isolate_);//形参std::vector>loaders_params={process_string(),FIXED_ONE_BYTE_STRING(isolate_,"getLinkedBinding"),FIXED_ONE_BYTE_STRING(isolate_,"getInternalBinding"),primordials_string()};//实例std::vector>loaders_args={process_object(),NewFunctionTemplate(binding::GetLinkedBinding)->GetFunction(context()).ToLocalChecked(),NewFunctionTemplate(binding::GetInternalBinding)->GetFunction(context()).ToLocalChecked(),primordials()};//执行internal/bootstrap/loaders.jsLocalloader_exports;if(!ExecuteBootstrapper(this,"internal/bootstrap/loaders",&loaders_params,&loaders_args).ToLocal(&loader_exports)){returnMaybeLocal();}//...}ExecuteBootstrapper会读取internal/bootstrap/loaders.js的内容,并封装到一个函数中,这个函数如下函数(process,getLinkedBinding,getInternalBinding,primordials){//internal/bootstrap/loaders.jscontent}然后执行这个参数,传入四个实际参数,看看internal/bootstrap/loaders.js执行后返回的是什么。constloaderExports={//LoadC++moduleinternalBinding,//NativeJSmodulemanagerNativeModule,//NativeJSloaderrequire:nativeModuleRequire};返回两个模块加载器和一个模块管理器。然后Node.js保存它们供以后使用。//保存函数执行的返回结果Localloader_exports;if(!ExecuteBootstrapper(this,"internal/bootstrap/loaders",&loaders_params,&loaders_args).ToLocal(&loader_exports)){returnMaybeLocal();}Local