成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊 Node.js 的模塊機制

開發 前端
模塊機制是 Node.js 中非常重要的組成,模塊機制使得我們可以以模塊化的方式寫代碼,而不是全部代碼都寫到一個文件里。

[[425593]]

本文轉載自微信公眾號「編程雜技」,作者theanarkh。轉載本文請聯系編程雜技公眾號。

前言:模塊機制是 Node.js 中非常重要的組成,模塊機制使得我們可以以模塊化的方式寫代碼,而不是全部代碼都寫到一個文件里。我們平時使用的比較多的通過 require 加載模塊,但是我們可能不是很清楚 require 的實現原理,另外 Node.js 里存在多種模塊類型,加載原理也不太一樣,本文將會介紹 Node.js 模塊機制以及實現原理。

1 模塊機制的初始化和使用

1.1 注冊 C++ 模塊

在 Node.js 啟動的時候,會通過 RegisterBuiltinModules 注冊 C++ 模塊。

  1. void RegisterBuiltinModules() {   
  2.  #define V(modname) _register_##modname();   
  3.    NODE_BUILTIN_MODULES(V)   
  4.  #undef V   

NODE_BUILTIN_MODULES是一個C語言宏,宏展開后如下(省略類似邏輯)

  1. voidRegisterBuiltinModules() {   
  2.     #define V(modname) _register_##modname();   
  3.       V(tcp_wrap)    
  4.       V(timers)   
  5.       ...其它模塊   
  6.     #undef V   

再一步展開如下

  1. void RegisterBuiltinModules() {   
  2.   _register_tcp_wrap();   
  3.   _register_timers();   

執行了一系列_register開頭的函數,但是我們在Node.js源碼里找不到這些函數,因為這些函數是在每個C++模塊定義的文件里(.cc文件的最后一行)通過宏定義的。以tcp_wrap模塊為例,看看它是怎么做的。文件tcp_wrap.cc的最后一句代碼 NODE_MODULE_CONTEXT_AWARE_INTERNAL(tcp_wrap, node::TCPWrap::Initialize) 宏展開是

  1. #define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc)  \   
  2.     NODE_MODULE_CONTEXT_AWARE_CPP(modname,  
  3.                                   regfunc,  
  4.                                   nullptr,  
  5.                                   NM_F_INTERNAL) 

繼續展開

  1. define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \   
  2.   static node::node_module _module = {              \   
  3.       NODE_MODULE_VERSION,                        \   
  4.       flags,                        \   
  5.       nullptr,                        \   
  6.       __FILE__,                        \   
  7.       nullptr,                        \   
  8.       (node::addon_context_register_func)(regfunc),  \   
  9.       NODE_STRINGIFY(modname),                        \   
  10.       priv,                        \   
  11.       nullptr};                        \   
  12.   void _register_tcp_wrap() { node_module_register(&_module); } 

我們看到每個C++模塊底層都定義了一個 _register 開頭的函數,在 Node.js 啟動時,就會把這些函數逐個執行一遍。我們繼續看一下這些函數都做了什么,在這之前,我們要先了解一下Node.js中表示 C++ 模塊的數據結構。

  1. struct node_module {   
  2.   int nm_version;   
  3.   unsigned int nm_flags;   
  4.   void* nm_dso_handle;   
  5.   const char* nm_filename;   
  6.   node::addon_register_func nm_register_func;   
  7.   node::addon_context_register_func nm_context_register_func;   
  8.   const char* nm_modname;   
  9.   void* nm_priv;   
  10.   struct node_module* nm_link;   
  11. }; 

我們看到 _register 開頭的函數調了 node_module_register,并傳入一個 node_module 數據結構,所以我們看一下node_module_register 的實現

  1. void node_module_register(void* m) {   
  2.       struct node_module* mp = reinterpret_cast<struct node_module*>(m);   
  3.       if (mp->nm_flags & NM_F_INTERNAL) {   
  4.         mp->nm_link = modlist_internal;   
  5.         modlist_internal = mp;   
  6.       } else if (!node_is_initialized) {  
  7.         mp->nm_flags = NM_F_LINKED;   
  8.         mp->nm_link = modlist_linked;   
  9.         modlist_linked = mp;   
  10.       } else {   
  11.         thread_local_modpending = mp;   
  12.       }   

C++ 內置模塊的 flag 是 NM_F_INTERNAL,所以會執行第一個if的邏輯,modlist_internal 類似一個頭指針。if 里的邏輯就是頭插法建立一個單鏈表。

1.2 初始化模塊加載器

注冊完 C++ 模塊后,接著初始化模塊加載器。

  1. MaybeLocal<Value> Environment::BootstrapInternalLoaders() { 
  2.   EscapableHandleScope scope(isolate_); 
  3.  
  4.   // 形參 
  5.   std::vector<Local<String>> loaders_params = { 
  6.       process_string(), 
  7.       FIXED_ONE_BYTE_STRING(isolate_, "getLinkedBinding"), 
  8.       FIXED_ONE_BYTE_STRING(isolate_, "getInternalBinding"), 
  9.       primordials_string()}; 
  10.   // 實參 
  11.   std::vector<Local<Value>> loaders_args = { 
  12.       process_object(), 
  13.       NewFunctionTemplate(binding::GetLinkedBinding) 
  14.           ->GetFunction(context()) 
  15.           .ToLocalChecked(), 
  16.       NewFunctionTemplate(binding::GetInternalBinding) 
  17.           ->GetFunction(context()) 
  18.           .ToLocalChecked(), 
  19.       primordials()}; 
  20.  
  21.   // 執行 internal/bootstrap/loaders.js 
  22.   Local<Value> loader_exports; 
  23.   if (!ExecuteBootstrapper( 
  24.            this, "internal/bootstrap/loaders", &loaders_params, &loaders_args) 
  25.            .ToLocal(&loader_exports)) { 
  26.     return MaybeLocal<Value>(); 
  27.   } 
  28.   // ... 

ExecuteBootstrapper 會讀取 internal/bootstrap/loaders.js 的內容,并且封裝到一個函數中,這個函數如下

  1. function (process, getLinkedBinding, getInternalBinding, primordials) { 
  2.     // internal/bootstrap/loaders.js 的內容 

然后執行這個參數,并傳入四個實參。我們看看 internal/bootstrap/loaders.js 執行后返回了什么。

  1. const loaderExports = { 
  2.   // 加載 C++ 模塊 
  3.   internalBinding, 
  4.   // 原生 JS 模塊管理器 
  5.   NativeModule, 
  6.   // 原生 JS 加載器 
  7.   require: nativeModuleRequire 
  8. }; 

返回了兩個模塊加載器和一個模塊管理器。接著 Node.js 把他們存起來,后續使用。

  1. // 保存函數執行的返回結果 
  2. Local<Value> loader_exports; 
  3. if (!ExecuteBootstrapper( 
  4.          this, "internal/bootstrap/loaders", &loaders_params, &loaders_args) 
  5.          .ToLocal(&loader_exports)) { 
  6.   return MaybeLocal<Value>(); 
  7. Local<Object> loader_exports_obj = loader_exports.As<Object>(); 
  8. // 獲取 C++ 模塊加載器 
  9. Local<Value> internal_binding_loader = loader_exports_obj->Get(context(), internal_binding_string()) 
  10.         .ToLocalChecked(); 
  11. // 保存 C++ 模塊加載器set_internal_binding_loader(internal_binding_loader.As<Function>()); 
  12. // 獲取原生 JS 加載器 
  13. Local<Value> require = loader_exports_obj->Get(context(), require_string()).ToLocalChecked(); 
  14. // 保存原生 JS 加載器set_native_module_require(require.As<Function>()); 

1.3 執行用戶 JS

Node.js 初始化完畢后最終會通過以下代碼執行用戶的代碼。

  1. StartExecution(env, "internal/main/run_main_module"

看看 StartExecution。

  1. MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) { 
  2.   EscapableHandleScope scope(env->isolate()); 
  3.   CHECK_NOT_NULL(main_script_id); 
  4.  
  5.   std::vector<Local<String>> parameters = { 
  6.       env->process_string(), 
  7.       // require 函數 
  8.       env->require_string(), 
  9.       env->internal_binding_string(), 
  10.       env->primordials_string(), 
  11.       FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete")}; 
  12.  
  13.   std::vector<Local<Value>> arguments = { 
  14.       env->process_object(), 
  15.       // 原生 JS 和 C++ 模塊加載器 
  16.       env->native_module_require(), 
  17.       env->internal_binding_loader(), 
  18.       env->primordials(), 
  19.       env->NewFunctionTemplate(MarkBootstrapComplete) 
  20.           ->GetFunction(env->context()) 
  21.           .ToLocalChecked()}; 
  22.  
  23.   return scope.EscapeMaybe( 
  24.       ExecuteBootstrapper(env, main_script_id, &parameters, &arguments)); 

傳入了兩個加載器,然后執行 run_main_module.js。核心代碼如下

  1. require('internal/modules/cjs/loader').Module.runMain(process.argv[1]); 

Module.runMain 的代碼如下

  1. function executeUserEntryPoint(main = process.argv[1]) { 
  2.   Module._load(main, nulltrue); 

最終通過 _load 完成用戶代碼的加載和執行,下面我們具體分析各種加載器。

2 模塊加載的實現

我們平時都是通過 require 加載模塊,require 幫我們處理一切,其實 Node.js 中有很多種類型的模塊,下面我們逐個介紹。

2.1 JSON 模塊

  1. Module._extensions['.json'] = function(module, filename) { 
  2.   const content = fs.readFileSync(filename, 'utf8'); 
  3.  
  4.   try { 
  5.     module.exports = JSONParse(stripBOM(content)); 
  6.   } catch (err) { 
  7.     err.message = filename + ': ' + err.message; 
  8.     throw err; 
  9.   } 
  10. }; 

JSON 模塊的實現很簡單,讀取文件的內容,解析一下就可以了。

2.2 用戶 JS 模塊

我們看到為什么在寫代碼的時候可以直接使用 require 函數,不是因為 require 是全局變量,而是我們寫的代碼會被封裝到一個函數里執行,require 和 module.exports 等變量都是函數的形參,在執行我們代碼時, Node.js 會傳入實參,所以我們就可以使用這些變量了。require 函數可以加載用戶自定義的 JS,也可以加載原生 JS,比如net,不過 Node.js 會優先查找原生 JS。

2.3 原生 JS 模塊

原生 JS 模塊和用戶 JS 模塊的加載原理是類似的,但是也有些不一樣的地方,我們看到執行原生 JS 模塊代碼時,傳入的實參和加載用戶 JS 時是不一樣的。首先 require 變量的值是一個原生 JS 模塊加載器,所以原生 JS 模塊里通過 require 只能加載 原生 JS 模塊。另外還有另一個實參也需要關注,那就是 internalBinding,internalBinding 用于加載 C++ 模塊,所以在原生 JS 里可以通過 internalBinding 加載 C++模塊。

2.4 C++ 模塊

2.5 Addon 模塊

 

 

 

后記:模塊機制在任何語言里都是非常基礎且重要的部分,深入理解 Node.js 的模塊機制原理,我們知道 require 的時候到時候發生了什么,如果你對模塊加載的具體實現感興趣,可以去閱讀 Node.js 的源碼,也可以看一下 https://github.com/theanarkh/js_runtime_loader 這個倉庫。

 

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2021-11-06 18:40:27

js底層模塊

2022-03-26 16:51:27

Node.jstrace架構

2020-08-31 15:00:17

Node.jsrequire前端

2025-05-26 00:31:31

2020-04-15 15:48:03

Node.jsstream前端

2022-03-30 08:36:32

Node.jsPRHTTP

2019-12-17 11:40:44

Node.js模塊前端

2020-11-02 11:40:24

Node.jsRequire前端

2023-06-30 23:25:46

HTTP模塊內存

2011-12-09 11:16:48

Node.js

2021-10-22 08:29:14

JavaScript事件循環

2017-08-16 10:36:10

JavaScriptNode.js事件驅動

2015-03-10 10:59:18

Node.js開發指南基礎介紹

2013-11-01 09:34:56

Node.js技術

2023-06-20 19:35:00

Node.js工具

2011-09-08 14:07:28

Node.js

2021-12-25 22:29:57

Node.js 微任務處理事件循環

2012-02-03 09:25:39

Node.js

2020-05-29 15:33:28

Node.js框架JavaScript

2021-01-26 08:07:44

Node.js模塊 Async
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99在线国产| 国产精品一区二区久久 | 男人的天堂视频网站 | 国产精品一区在线观看 | 国产中文字幕在线 | 中文字幕第一页在线 | 精品91久久久 | 一级免费毛片 | 久久一区二区精品 | 国产一区二区三区免费 | 精品精品 | 激情五月综合 | 黄色毛片免费视频 | 欧美国产日韩在线观看 | 亚洲精品久久久久avwww潮水 | 中文字幕不卡视频在线观看 | 免费三级av | 永久免费在线观看 | 欧美中文一区 | 精品国产欧美一区二区三区成人 | 欧美日韩综合一区 | 国产精品久久久久久久久久软件 | 一区二区三区日本 | 在线成人精品视频 | 成人在线观看免费 | 日韩黄a | 成人小视频在线观看 | 一区二区在线免费观看 | 一区二区三区视频在线 | 日韩一区二区三区在线 | 久草在线免费资源 | 国产精品国产精品国产专区不片 | 久久久久久久久久久久久久久久久久久久 | 午夜视频一区二区三区 | 在线一区视频 | 成人国产精品一级毛片视频毛片 | www.成人.com | 精品99在线 | 国产精品久久久久久久久久 | 国产精品久久国产精品99 | 自拍偷拍亚洲欧美 |