得到hello.node結果文件后,如果調用擴展模塊其實在前面已經提及。require()方法通過解析標識符、路徑分析、文件定位,然后加載執行即可。下面的代碼引入前面編譯得到的.node文件,并調用執行其中的方法:
~~~
const hello = require("./build/Release/hello.node");
console.log(hello.sayHello());
~~~
以上代碼存為hello.js,調用`node hello.js`命名即可得到如下輸出結果:
~~~
Hello World!
~~~
對于以.node為擴展名的文件,Node將會調用process.dlopen() 方法去加載文件:
~~~
//Native extension for .node
Module._extensions['.node'] = process.dlopen;
~~~
對于調用者而言,require()是輕松愉快的。對于擴展模塊的編寫者來說,process.dlopen()中隱含的過程值得了解一番。如圖:

require()在引入.node文件的過程中,實際上經歷了4個層面上的調用。
加載.node文件實際上經歷了兩個步驟,第一個步驟是調用uv_dlopen()方法去打開動態鏈接庫,第二個步驟是調用uv_dlsym()方法找到動態鏈接庫中通過NODE_MODULE宏定義的方法地址。這兩個過程都是通過libuv庫進行封裝的:在`*nix`平臺下實際上調用的是dlfcn.h頭文件中定義的dlopen()和dlsym()兩個方法;在Windows平臺則是通過LoadLibraryExW()和GetProcAddress()這兩個方法實現的,它們分別加載.so和.dll文件(實際為.node文件)。
這里對libuv函數的調用充分表現Node利用libuv實現跨平臺的方式,這樣的情景在很多地方還會出現。
由于編寫模塊時通過NODE_MODULE將模塊定義為`node_module_struct`結構,所以在獲取函數地址之后,將它映射為`node_module_struct`結構幾乎是無縫對接的。接下來的過程就是將傳入的exports對象作為實參運行,將C++中定義的方法掛載在exports對象上,然后調用者就可以輕松調用了。
C/C++擴展模塊與JavaScript模塊的區別在于加載之后不需要編譯,直接執行之后就可以被外部調用了,其加載速度比JavaScript模塊略快。
使用C/C++擴展的一個好處就在于可以靈活和動態的加載它們,保持Node模塊自身簡單性的同時,給予Node無限的可擴展性。
關于node-gyp工具的更多細節可以參見 https://github.com/TooTallNate/node-gyp(作者為Nathan Rajlich,Node源碼的核心貢獻者之一)。
- 目錄
- 第1章 Node 簡介
- 1.1 Node 的誕生歷程
- 1.2 Node 的命名與起源
- 1.2.1 為什么是 JavaScript
- 1.2.2 為什么叫 Node
- 1.3 Node給JavaScript帶來的意義
- 1.4 Node 的特點
- 1.4.1 異步 I/O
- 1.4.2 事件與回調函數
- 1.4.3 單線程
- 1.4.4 跨平臺
- 1.5 Node 的應用場景
- 1.5.1 I/O 密集型
- 1.5.2 是否不擅長CPU密集型業務
- 1.5.3 與遺留系統和平共處
- 1.5.4 分布式應用
- 1.6 Node 的使用者
- 1.7 參考資源
- 第2章 模塊機制
- 2.1 CommonJS 規范
- 2.1.1 CommonJS 的出發點
- 2.1.2 CommonJS 的模塊規范
- 2.2 Node 的模塊實現
- 2.2.1 優先從緩存加載
- 2.2.2 路徑分析和文件定位
- 2.2.3 模塊編譯
- 2.3 核心模塊
- 2.3.1 JavaScript核心模塊的編譯過程
- 2.3.2 C/C++核心模塊的編譯過程
- 2.3.3 核心模塊的引入流程
- 2.3.4 編寫核心模塊
- 2.4 C/C++擴展模塊
- 2.4.1 前提條件
- 2.4.2 C/C++擴展模塊的編寫
- 2.4.3 C/C++擴展模塊的編譯
- 2.4.2 C/C++擴展模塊的加載
- 2.5 模塊調用棧
- 2.6 包與NPM
- 2.6.1 包結構
- 2.6.2 包描述文件與NPM
- 2.6.3 NPM常用功能
- 2.6.4 局域NPM
- 2.6.5 NPM潛在問題
- 2.7 前后端共用模塊
- 2.7.1 模塊的側重點
- 2.7.2 AMD規范
- 2.7.3 CMD規范
- 2.7.4 兼容多種模塊規范
- 2.8 總結
- 2.9 參考資源
- 第3章 異步I/O
- 3.1 為什么要異步I/O
- 3.1.1 用戶體驗
- 3.1.2 資源分配
- 3.2 異步I/O實現現狀
- 3.2.1 異步I/O與非阻塞I/O
- 3.2.2 理想的非阻塞異步I/O
- 3.2.3 現實的異步I/O
- 3.3 Node的異步I/O
- 3.3.1 事件循環
- 3.3.2 觀察者
- 3.3.3 請求對象
- 3.3.4 執行回調
- 3.3.5 小結
- 3.4 非I/O的異步API
- 3.4.1 定時器
- 3.5 事件驅動與高性能服務器