这一小节是第二章的延伸,朴灵作者在这里介绍当JavaScript性能不足时,如何用C/C++写原生扩展模块(Native Addon)来提升Node的性能。核心模块(如fs、crypto)很多就是用C++实现的,但我们自己也可以写自定义的.node模块。这在2013年书出版时是常见的做法(现在有N-API、更稳定的方式,但原理类似)。
这一小节内容相对实践性强,作者通过一个简单示例演示了从编写C++代码到编译成.node文件,再到在JS中require的过程。适合对性能优化感兴趣的开发者。
详细讲解
为什么需要C/C++扩展模块?
- JavaScript适合I/O密集,但CPU密集任务(如复杂计算、图像处理)慢。
- Node允许用C++写addon,暴露给JS调用,结合V8(JS引擎)和libuv(异步I/O)。
- 扩展模块以**.node**为后缀,像普通模块一样require('mymodule.node')。
扩展模块的加载方式
- 在路径分析时,如果文件是.node扩展,用process.dlopen加载(类似动态链接库)。
- 与.js不同,不经过包裹函数,直接执行C++初始化函数。
示例:Hello World扩展
书里用一个简单示例演示(类似V8的Hello World):
-
C++代码(addon.cc):
#include <node.h> #include <v8.h> using namespace v8; void Method(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world").ToLocalChecked()); } void init(Local<Object> exports) { NODE_SET_METHOD(exports, "hello", Method); } NODE_MODULE(addon, init) // 关键宏:定义模块名和初始化函数 -
编译:用binding.gyp(GYP工具,书时Node用这个,现在多用node-gyp + CMake):
{ "targets": [{ "target_name": "addon", "sources": ["addon.cc"] }] }命令:
node-gyp configure build→ 生成build/Release/addon.node -
JS中使用:
var addon = require('./build/Release/addon'); console.log(addon.hello()); // 'hello world'
关键概念
- V8 API:操作JS对象(String、Object、Function等)。
- NODE_MODULE宏:注册模块初始化函数。
- 数据类型转换:C++和JS之间(如Number、Array)。
- 线程注意:不能阻塞V8主线程,复杂计算用libuv队列。
注意事项
- 兼容性:不同Node版本V8不同,addon可能不兼容(现在N-API解决)。
- 调试难:用gdb或lldb。
- 性能:适合热点代码。