原生模块集成 #
一、原生模块概述 #
1.1 什么是原生模块 #
原生模块是用 C/C++ 编写的 Node.js 模块,通过 Node.js 的 N-API 或 NAN 接口与 JavaScript 交互。
text
┌─────────────────────────────────────────┐
│ JavaScript 代码 │
└────────────────┬────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────┐
│ 原生模块 (C/C++) │
│ │
│ - 系统级 API │
│ - 高性能计算 │
│ - 硬件访问 │
└────────────────┬────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────┐
│ 操作系统 / 硬件 │
└─────────────────────────────────────────┘
1.2 为什么使用原生模块 #
| 场景 | 说明 |
|---|---|
| 性能要求高 | 计算密集型任务 |
| 系统调用 | 访问操作系统 API |
| 硬件交互 | 与硬件设备通信 |
| 代码复用 | 使用现有 C/C++ 库 |
二、使用现有原生模块 #
2.1 安装原生模块 #
bash
# 安装需要编译的原生模块
npm install better-sqlite3
npm install sharp
npm install node-ffi-napi
2.2 配置 electron-rebuild #
bash
# 安装 electron-rebuild
npm install @electron/rebuild --save-dev
# 重新编译原生模块
npx @electron/rebuild
# 或在 package.json 中配置
{
"scripts": {
"postinstall": "electron-rebuild"
}
}
2.3 配置 package.json #
json
{
"scripts": {
"postinstall": "electron-rebuild",
"rebuild": "electron-rebuild -f -w better-sqlite3"
},
"dependencies": {
"better-sqlite3": "^9.0.0"
},
"devDependencies": {
"@electron/rebuild": "^3.0.0"
}
}
2.4 使用原生模块 #
javascript
// 使用 better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('mydb.db');
// 使用 sharp
const sharp = require('sharp');
await sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg');
三、开发原生模块 #
3.1 项目结构 #
text
my-native-module/
├── src/
│ └── addon.cc # C++ 源码
├── index.js # JavaScript 入口
├── binding.gyp # node-gyp 配置
├── package.json
└── README.md
3.2 binding.gyp 配置 #
python
{
"targets": [
{
"target_name": "addon",
"sources": ["src/addon.cc"],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
"dependencies": [
"<!(node -p \"require('node-addon-api').gyp\")"
],
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
"conditions": [
["OS=='win'", {
"defines": ["WIN32"]
}],
["OS=='mac'", {
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "10.15"
}
}]
]
}
]
}
3.3 C++ 模块示例 #
cpp
// src/addon.cc
#include <napi.h>
// 简单函数
Napi::String Hello(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::String::New(env, "Hello from C++!");
}
// 带参数的函数
Napi::Number Add(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) {
Napi::TypeError::New(env, "Two numbers expected").ThrowAsJavaScriptException();
return Napi::Number();
}
double a = info[0].As<Napi::Number>().DoubleValue();
double b = info[1].As<Napi::Number>().DoubleValue();
return Napi::Number::New(env, a + b);
}
// 异步函数
class AsyncWorker : public Napi::AsyncWorker {
public:
AsyncWorker(Napi::Env& env, const std::string& input)
: Napi::AsyncWorker(env), deferred(Napi::Promise::Deferred::New(env)), input(input) {}
void Execute() override {
// 在工作线程中执行
result = "Processed: " + input;
}
void OnOK() override {
// 在主线程中返回结果
deferred.Resolve(Napi::String::New(Env(), result));
}
void OnError(const Napi::Error& e) override {
deferred.Reject(e.Value());
}
Napi::Promise GetPromise() { return deferred.Promise(); }
private:
Napi::Promise::Deferred deferred;
std::string input;
std::string result;
};
Napi::Promise ProcessAsync(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
std::string input = info[0].As<Napi::String>().Utf8Value();
AsyncWorker* worker = new AsyncWorker(env, input);
worker->Queue();
return worker->GetPromise();
}
// 导出模块
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("hello", Napi::Function::New(env, Hello));
exports.Set("add", Napi::Function::New(env, Add));
exports.Set("processAsync", Napi::Function::New(env, ProcessAsync));
return exports;
}
NODE_API_MODULE(addon, Init)
3.4 JavaScript 封装 #
javascript
// index.js
const addon = require('./build/Release/addon.node');
module.exports = {
hello: addon.hello,
add: addon.add,
processAsync: addon.processAsync
};
3.5 使用自定义模块 #
javascript
const myAddon = require('my-native-module');
// 同步调用
console.log(myAddon.hello());
console.log(myAddon.add(1, 2));
// 异步调用
const result = await myAddon.processAsync('input data');
console.log(result);
四、编译配置 #
4.1 安装编译工具 #
bash
# Windows
npm install --global windows-build-tools
# macOS
xcode-select --install
# Linux
sudo apt-get install build-essential
4.2 编译命令 #
bash
# 配置
node-gyp configure
# 编译
node-gyp build
# 重新编译
node-gyp rebuild
# 清理
node-gyp clean
4.3 跨平台编译 #
json
// package.json
{
"scripts": {
"build": "node-gyp rebuild",
"build:win": "node-gyp rebuild --target_arch=ia32",
"build:mac": "node-gyp rebuild --target_arch=x64",
"build:linux": "node-gyp rebuild"
}
}
五、Electron 集成 #
5.1 配置 electron-rebuild #
json
// package.json
{
"scripts": {
"postinstall": "electron-rebuild",
"rebuild": "electron-rebuild -f -w my-native-module"
},
"dependencies": {
"my-native-module": "file:./local-module"
}
}
5.2 预编译二进制 #
bash
# 使用 prebuild
npm install prebuild --save-dev
# 生成预编译二进制
prebuild --all
# 上传到 GitHub Releases
prebuild --upload <token>
5.3 使用预编译二进制 #
json
// package.json
{
"dependencies": {
"my-native-module": "^1.0.0"
},
"scripts": {
"install": "prebuild-install || node-gyp rebuild"
}
}
六、FFI 集成 #
6.1 使用 node-ffi-napi #
bash
npm install ffi-napi ref-napi
javascript
const ffi = require('ffi-napi');
const ref = require('ref-napi');
// 定义类型
const intPtr = ref.refType(ref.types.int);
const stringPtr = ref.refType(ref.types.CString);
// 加载动态库
const lib = ffi.Library('mylib', {
'add': ['int', ['int', 'int']],
'get_string': ['string', []],
'process_data': ['int', [stringPtr, int]]
});
// 调用函数
const result = lib.add(1, 2);
console.log(result);
const str = lib.get_string();
console.log(str);
6.2 调用系统 API #
javascript
const ffi = require('ffi-napi');
const ref = require('ref-napi');
// Windows API
if (process.platform === 'win32') {
const user32 = ffi.Library('user32', {
'MessageBoxA': ['int', ['int', 'string', 'string', 'int']]
});
user32.MessageBoxA(0, 'Hello from Node.js!', 'Message', 0);
}
// macOS API
if (process.platform === 'darwin') {
const coreFoundation = ffi.Library('CoreFoundation', {
// 定义函数
});
}
七、最佳实践 #
7.1 错误处理 #
cpp
Napi::Value SafeFunction(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
try {
// 执行操作
return DoSomething();
} catch (const std::exception& e) {
Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
return env.Null();
}
}
7.2 内存管理 #
cpp
// 使用 Napi::Buffer 管理内存
Napi::Buffer<uint8_t> CreateBuffer(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// 创建数据
uint8_t* data = new uint8_t[1024];
// 创建 Buffer,自动管理内存
return Napi::Buffer<uint8_t>::New(env, data, 1024, [](Napi::Env env, uint8_t* data) {
delete[] data; // 自动释放
});
}
7.3 线程安全 #
cpp
// 使用 Napi::ThreadSafeFunction
class ThreadSafeExample {
public:
ThreadSafeExample(Napi::Env env, Napi::Function callback) {
tsfn = Napi::ThreadSafeFunction::New(
env,
callback,
"ThreadSafeExample",
0,
1
);
}
void CallFromThread() {
tsfn.BlockingCall([](Napi::Env env, Napi::Function jsCallback) {
jsCallback.Call({Napi::String::New(env, "from thread")});
});
}
private:
Napi::ThreadSafeFunction tsfn;
};
八、调试 #
8.1 编译调试版本 #
bash
node-gyp rebuild --debug
8.2 使用日志 #
cpp
#include <iostream>
Napi::Value DebugFunction(const Napi::CallbackInfo& info) {
std::cout << "Debug: function called" << std::endl;
// ...
}
8.3 使用 VS Code 调试 #
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Native Addon",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/test.js",
"nativeDebug": true
}
]
}
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| electron-rebuild | 重新编译原生模块 |
| node-addon-api | 推荐的 C++ API |
| binding.gyp | 编译配置文件 |
| 跨平台 | 处理不同平台差异 |
| 预编译 | 提供预编译二进制 |
9.2 下一步 #
现在你已经掌握了原生模块集成,接下来让我们学习 调试技巧,深入了解 Electron 应用的调试方法!
最后更新:2026-03-28