跳到主要内容

napi-rs 与 Node.js 原生模块

问题

napi-rs 是什么?如何用 Rust 编写 Node.js 原生模块?

答案

napi-rs 是用 Rust 编写 Node.js 原生模块的框架,基于 Node-API(N-API),提供了类型安全的绑定。SWC、Rspack、Biome 等工具都通过 napi-rs 与 Node.js 生态集成。

为什么用 Rust 写 Node.js 原生模块

方案语言安全性性能开发体验
纯 JSJavaScript安全
C++ AddonC++手动管理内存最快
napi-rsRust内存安全接近 C++
WebAssemblyRust/C++安全较快(有开销)中等

快速上手

# 创建项目
npx @napi-rs/cli new my-napi-module
cd my-napi-module
src/lib.rs
use napi_derive::napi;

/// 同步函数:直接导出
#[napi]
pub fn sum(a: i32, b: i32) -> i32 {
a + b
}

/// 异步函数:返回 AsyncTask
#[napi]
pub async fn read_file_async(path: String) -> napi::Result<String> {
tokio::fs::read_to_string(&path)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))
}

/// 带回调的流式处理
#[napi(ts_args_type = "callback: (line: string) => void")]
pub fn process_lines(input: String, callback: napi::JsFunction) {
for line in input.lines() {
callback
.call(None, &[line.into()])
.unwrap();
}
}
自动生成的 TypeScript 类型
// index.d.ts(napi-rs 自动生成)
export function sum(a: number, b: number): number
export function readFileAsync(path: string): Promise<string>
export function processLines(input: string, callback: (line: string) => void): void
在 Node.js 中使用
import { sum, readFileAsync } from './index.js';

console.log(sum(1, 2)); // 3
const content = await readFileAsync('./package.json');

napi-rs 的跨平台分发

napi-rs 的 CLI 工具支持预编译 + 按平台发布 npm 包:

@my-scope/my-module              # 主包(选择性加载)
@my-scope/my-module-darwin-arm64 # macOS ARM
@my-scope/my-module-darwin-x64 # macOS Intel
@my-scope/my-module-linux-x64 # Linux x64
@my-scope/my-module-win32-x64 # Windows x64

用户 npm install @my-scope/my-module 时,自动下载对应平台的预编译二进制,无需本地编译


常见面试问题

Q1: napi-rs vs WebAssembly,哪个更适合 Node.js 扩展?

答案

维度napi-rsWebAssembly
性能原生速度有 Wasm 运行时开销
系统调用✅ 完全访问❌ 受限沙箱
文件/网络✅ 直接调用❌ 需要通过 JS
跨平台需要为每个平台编译✅ 一次编译到处运行
分发多平台预编译 npm 包单个 .wasm 文件

选择建议

  • 需要系统调用(文件、网络、OS API)→ napi-rs
  • 纯计算(加密、编解码、算法)→ WebAssembly
  • 需要极致性能 → napi-rs

相关链接