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

聊聊 Node.js 的模塊機(jī)制

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

[[425593]]

本文轉(zhuǎn)載自微信公眾號(hào)「編程雜技」,作者theanarkh。轉(zhuǎn)載本文請(qǐng)聯(lián)系編程雜技公眾號(hào)。

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

1 模塊機(jī)制的初始化和使用

1.1 注冊(cè) C++ 模塊

在 Node.js 啟動(dòng)的時(shí)候,會(huì)通過(guò) RegisterBuiltinModules 注冊(cè) C++ 模塊。

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

NODE_BUILTIN_MODULES是一個(gè)C語(yǔ)言宏,宏展開(kāi)后如下(省略類似邏輯)

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

再一步展開(kāi)如下

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

執(zhí)行了一系列_register開(kāi)頭的函數(shù),但是我們?cè)贜ode.js源碼里找不到這些函數(shù),因?yàn)檫@些函數(shù)是在每個(gè)C++模塊定義的文件里(.cc文件的最后一行)通過(guò)宏定義的。以tcp_wrap模塊為例,看看它是怎么做的。文件tcp_wrap.cc的最后一句代碼 NODE_MODULE_CONTEXT_AWARE_INTERNAL(tcp_wrap, node::TCPWrap::Initialize) 宏展開(kāi)是

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

繼續(xù)展開(kāi)

  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); } 

我們看到每個(gè)C++模塊底層都定義了一個(gè) _register 開(kāi)頭的函數(shù),在 Node.js 啟動(dòng)時(shí),就會(huì)把這些函數(shù)逐個(gè)執(zhí)行一遍。我們繼續(xù)看一下這些函數(shù)都做了什么,在這之前,我們要先了解一下Node.js中表示 C++ 模塊的數(shù)據(jù)結(jié)構(gòu)。

  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 開(kāi)頭的函數(shù)調(diào)了 node_module_register,并傳入一個(gè) node_module 數(shù)據(jù)結(jié)構(gòu),所以我們看一下node_module_register 的實(shí)現(xiàn)

  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++ 內(nèi)置模塊的 flag 是 NM_F_INTERNAL,所以會(huì)執(zhí)行第一個(gè)if的邏輯,modlist_internal 類似一個(gè)頭指針。if 里的邏輯就是頭插法建立一個(gè)單鏈表。

1.2 初始化模塊加載器

注冊(cè)完 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.   // 實(shí)參 
  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.   // 執(zhí)行 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 會(huì)讀取 internal/bootstrap/loaders.js 的內(nèi)容,并且封裝到一個(gè)函數(shù)中,這個(gè)函數(shù)如下

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

然后執(zhí)行這個(gè)參數(shù),并傳入四個(gè)實(shí)參。我們看看 internal/bootstrap/loaders.js 執(zhí)行后返回了什么。

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

返回了兩個(gè)模塊加載器和一個(gè)模塊管理器。接著 Node.js 把他們存起來(lái),后續(xù)使用。

  1. // 保存函數(shù)執(zhí)行的返回結(jié)果 
  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 執(zhí)行用戶 JS

Node.js 初始化完畢后最終會(huì)通過(guò)以下代碼執(zhí)行用戶的代碼。

  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 函數(shù) 
  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)); 

傳入了兩個(gè)加載器,然后執(zhí)行 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); 

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

2 模塊加載的實(shí)現(xiàn)

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

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 模塊的實(shí)現(xiàn)很簡(jiǎn)單,讀取文件的內(nèi)容,解析一下就可以了。

2.2 用戶 JS 模塊

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

2.3 原生 JS 模塊

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

2.4 C++ 模塊

2.5 Addon 模塊

 

 

 

后記:模塊機(jī)制在任何語(yǔ)言里都是非常基礎(chǔ)且重要的部分,深入理解 Node.js 的模塊機(jī)制原理,我們知道 require 的時(shí)候到時(shí)候發(fā)生了什么,如果你對(duì)模塊加載的具體實(shí)現(xiàn)感興趣,可以去閱讀 Node.js 的源碼,也可以看一下 https://github.com/theanarkh/js_runtime_loader 這個(gè)倉(cāng)庫(kù)。

 

責(zé)任編輯:武曉燕 來(lái)源: 編程雜技
相關(guān)推薦

2021-11-06 18:40:27

js底層模塊

2022-03-26 16:51:27

Node.jstrace架構(gòu)

2020-08-31 15:00:17

Node.jsrequire前端

2020-04-15 15:48:03

Node.jsstream前端

2025-05-26 00:31:31

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模塊內(nèi)存

2017-08-16 10:36:10

JavaScriptNode.js事件驅(qū)動(dòng)

2021-10-22 08:29:14

JavaScript事件循環(huán)

2011-12-09 11:16:48

Node.js

2013-11-01 09:34:56

Node.js技術(shù)

2015-03-10 10:59:18

Node.js開(kāi)發(fā)指南基礎(chǔ)介紹

2011-09-08 14:07:28

Node.js

2023-06-20 19:35:00

Node.js工具

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

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
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 免费观看一级特黄欧美大片 | 一区二区三区视频在线观看 | 日日综合 | 欧美成人一级 | 久久久久亚洲精品 | 红色av社区| 欧美在线一区二区三区 | 日日爱av| 久久高清精品 | 伊人精品 | 久久久精品影院 | 伊人中文字幕 | 亚洲 欧美 日韩 精品 | 亚洲一区二区三区桃乃木香奈 | 欧美一级特黄aaa大片在线观看 | 四虎在线视频 | 欧美激情在线精品一区二区三区 | 99视频在线免费观看 | 国产成人免费观看 | 99久久精品视频免费 | 在线观看免费av网 | 国产在线观 | a级在线 | 国产精品综合网 | 亚洲精品68久久久一区 | 综合二区 | 成人av电影免费在线观看 | 九九导航| 久久这里只有精品首页 | 日本电影韩国电影免费观看 | 91在线观看免费 | 国产精品一区二区不卡 | 久久久久国产一区二区三区 | 视频第一区| 国产精品久久久久久久久久 | 日韩在线视频网址 | 国产精品久久久久久久久久久久久 | 久久精品亚洲一区二区三区浴池 | 色吊丝2288sds中文字幕 | 欧美日一区| 亚洲一区二区在线播放 |