🌐 Anatomy of a Node-API project
在深入代码之前,了解每个 node-addon-api 项目共享的文件和布局会很有帮助。本指南中的所有 N-API 示例都遵循相同的结构,因此本页将进行一次解释,以便那些教程可以专注于每个示例的独特之处。
🌐 Before diving into code, it helps to understand the files and layout that every node-addon-api project shares. All N-API examples in these guides follow this same structure, so this page explains it once so those tutorials can focus on what makes each one unique.
🌐 Choosing an API level
Node-API 在两个层面上运行:
🌐 Node-API operates at two levels:
- C API 直接内置于 Node.js 中,并在 Node.js API 页面 上有完整文档。让你在无需额外依赖的情况下获得最大的控制权。
- C++ 封装器(
node-addon-api) 是一个 npm 包,它将 C API 封装在符合惯用的 C++ 对象模型中。推荐用于大多数项目:它消除了大量模板代码,同时保留了 Node-API 的完整 ABI 稳定性保证。
本节中的教程使用 node-addon-api。
🌐 The tutorials in this section use node-addon-api.
Node-API 在所有当前支持的 Node.js 版本中都是稳定的。为了获得最佳体验,请使用 Active LTS 或 Maintenance LTS 版本。你可以使用
node -v来检查你正在运行的 Node.js 版本。
🌐 Directory layout
.
├── binding.gyp # tells node-gyp how to compile the C/C++ source
├── build/ # compiled output (generated)
├── lib/
│ └── binding.js # JavaScript layer that loads the compiled binary
├── node_modules/
├── src/
│ └── *.cc / *.h # your C/C++ implementation
├── test/
│ └── *.js # test code
├── package.json
└── package-lock.json
package.json 中的两个条目是特定于本地插件的。
🌐 Two entries in package.json are specific to native addons.
🌐 node-addon-api dependency
"dependencies": {
"node-addon-api": "^8.0.0"
}node-addon-api 为 Node.js 内置的 C API 添加了一个 C++ 封装。它使在 C++ 中创建和操作 JavaScript 对象变得简单,即使你封装的底层库是用 C 编写的,它也非常有用。
"gypfile": true这告诉 npm 该包需要一个本地编译步骤。当 npm 看到这个条目时,它会自动调用打包的 node-gyp 副本,该副本读取 binding.gyp 来构建二进制文件。
🌐 This tells npm that the package requires a native compilation step. When npm sees this entry it automatically invokes its bundled copy of node-gyp, which reads binding.gyp to build the binary.
binding.gyp 是一个 GYP 文件,用于描述如何编译和链接你的 C/C++ 代码。它的名称必须正好是 binding.gyp。
GYP(生成你的项目)允许你编写一个可以在 Windows、macOS 和 Linux 上使用的单一构建描述文件。node-gyp 读取此文件并生成适合平台的构建文件(Windows 上的 MSVC 项目、Linux 上的 Makefile、macOS 上的 Xcode 项目),然后调用编译器。
一个 node-addon-api 项目的最小 binding.gyp 看起来像这样:
🌐 A minimal binding.gyp for a node-addon-api project looks like this:
{
"targets": [
{
"target_name": "my_addon",
"sources": ["src/my_addon.cc"],
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"]
}
]
}完整的 GYP 格式在 GYP 用户文档 中有详细说明。
🌐 The full GYP format is documented in the GYP User Documentation.
lib/ 目录包含一个轻量的 JavaScript 封装器,它加载已编译的二进制文件并重新导出。该层将二进制加载逻辑集中在一个地方,并为你提供一个自然的位置来添加 JavaScript 端的验证或便捷方法。
🌐 The lib/ directory holds a thin JavaScript wrapper that loads the compiled binary and re-exports it. This layer keeps the binary-loading logic in one place and gives you a natural spot to add JavaScript-side validation or convenience methods.
一个典型的 binding.js 使用 bindings 包来解析 .node 文件的路径,无论平台如何:
🌐 A typical binding.js uses the bindings package to resolve the path to the .node file regardless of platform:
'use strict';
const addon = require('bindings')('my_addon');
module.exports = addon;如果你使用 npm 全局安装包并遇到
Error: EACCES: permission denied,请使用nvm来管理你的 Node.js 安装。使用 nvm 时,全局安装会进入你的主目录,并且永远不需要sudo。