企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
得到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()中隐含的过程值得了解一番。如图: ![](https://box.kancloud.cn/2016-08-26_57bf1fe42ae1f.png) 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源码的核心贡献者之一)。