Node.js 与底层交互 (N-API / Wasm)
1. 集成方案选择
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| N-API (Native Addon) | 极致性能、系统级 API | 最高性能 | 需要编译、平台相关 |
| WebAssembly | 纯计算逻辑移植 | 跨平台、安全 | 不能调用系统 API |
| FFI (node-ffi-napi) | 快速调用动态库 | 无需 C++ 胶水代码 | 性能有损耗 |
2. N-API (Node-API) 🔥
解决的历史痛点
在 N-API 出现之前:
- Native Addon 直接依赖 V8 API
- Node.js 升级 → V8 版本变更 → API 变化 → 所有 Addon 重新编译/重写
ABI 稳定性
┌─────────────────────────────────────────────────────┐
│ JavaScript │
├─────────────────────────────────────────────────────┤
│ N-API │ ← 稳定的 C 抽象层
├─────────────────────────────────────────────────────┤
│ V8 (可能变化) │ 其他引擎 (未来可能) │
└─────────────────────────────────────────────────────┘IMPORTANT
使用 N-API 编写的插件,编译一次,多个 Node.js 主版本可用。
基本示例
cpp
// addon.cc
#include <napi.h>
Napi::Value Add(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
double a = info[0].As<Napi::Number>().DoubleValue();
double b = info[1].As<Napi::Number>().DoubleValue();
return Napi::Number::New(env, a + b);
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("add", Napi::Function::New(env, Add));
return exports;
}
NODE_API_MODULE(addon, Init)javascript
// 使用
const addon = require('./build/Release/addon');
console.log(addon.add(1, 2)); // 3构建配置 (node-gyp)
python
# binding.gyp
{
"targets": [{
"target_name": "addon",
"sources": ["addon.cc"],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"]
}]
}3. WebAssembly 🔥
适用场景
- 纯计算逻辑 (加密、图像处理、算法)
- 已有 C/C++/Rust 代码库移植
- 需要浏览器和 Node.js 同时运行
基本使用
javascript
const fs = require('fs');
// 加载 Wasm 模块
const wasmBuffer = fs.readFileSync('./module.wasm');
const wasmModule = new WebAssembly.Module(wasmBuffer);
const wasmInstance = new WebAssembly.Instance(wasmModule);
// 调用导出函数
const result = wasmInstance.exports.add(1, 2);
console.log(result);使用 AssemblyScript (TypeScript → Wasm)
typescript
// assembly/index.ts
export function fibonacci(n: i32): i32 {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}bash
# 编译
npx asc assembly/index.ts -o build/module.wasmjavascript
// 使用
const loader = require('@assemblyscript/loader');
const wasm = await loader.instantiate(
fs.readFileSync('./build/module.wasm')
);
console.log(wasm.exports.fibonacci(40));性能对比
| 任务 | 纯 JS | WebAssembly | 提升 |
|---|---|---|---|
| Fibonacci(40) | 1200ms | 300ms | 4x |
| 图像模糊 | 800ms | 200ms | 4x |
| MD5 哈希 | 100ms | 20ms | 5x |
4. FFI (Foreign Function Interface)
快速调用动态库
javascript
const ffi = require('ffi-napi');
const ref = require('ref-napi');
// 调用系统 C 库
const libm = ffi.Library('libm', {
'ceil': ['double', ['double']],
'floor': ['double', ['double']]
});
console.log(libm.ceil(1.5)); // 2
console.log(libm.floor(1.5)); // 1适用场景
- 快速原型验证
- 调用已有的 .dll / .so 文件
- 不想写 C++ 胶水代码
5. 实战场景举例
图片处理: Sharp
javascript
// Sharp 底层使用 libvips (C++),通过 N-API 暴露接口
const sharp = require('sharp');
await sharp('input.jpg')
.resize(200, 200)
.toFile('output.jpg');
// 性能比纯 JS 的 Jimp 快 10-100 倍加密: bcrypt
javascript
// bcrypt 使用 C++ 实现
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash('password', 10);