原生模块集成 #

一、原生模块概述 #

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