附加组件(Addons)是用 C++ 编写的动态链接共享对象。require() 函数可以将附加组件作为普通的 Node.js 模块加载。附加组件提供了 JavaScript 和 C/C++ 库之间的接口。
实现附加组件有三种选择:
除非需要直接访问 Node-API 未公开的功能,否则请使用 Node-API。
当不使用 Node-API 时,实现附加组件会变得更加复杂,需要了解多个组件和 API:
v8.h 头文件(Node.js 源代码树中的 deps/v8/include/v8.h)中,也可在网上获取。node::ObjectWrap 类。deps/ 目录中。只有 libuv、OpenSSL、V8 和 zlib 符号是 Node.js 特意重新导出的,附加组件可以在不同程度上使用它们。注意事项
所有 Node.js 扩展都必须按照以下模式导出一个初始化函数:
NODE_MODULE 后面没有分号,因为它不是一个函数(参见 node.h)。
module_name 必须与最终二进制文件的文件名(不包括 .node 后缀)匹配。
在 hello.cc 示例中,初始化函数是 Initialize,扩展模块名称是 addon。
当使用 node-gyp 构建扩展时,将宏 NODE_GYP_MODULE_NAME 用作 NODE_MODULE() 的第一个参数,将确保最终二进制文件的名称会被传递给 NODE_MODULE()。
用 NODE_MODULE() 定义的扩展不能同时在多个上下文或多个线程中加载。
Initialize 的函数(名字可以改,但通常约定用这个),它的作用是把 C/C++ 里的功能(比如函数、变量)暴露给 Node.js 调用(通过 exports 对象,类似 Node.js 里的 module.exports)。NODE_MODULE(...) 这个宏来“注册”这个扩展,告诉 Node.js:“这是我的扩展,入口是 Initialize 函数”。addon)必须和最终编译出来的二进制文件名字一致(比如编译后叫 addon.node,那名字就是 addon)。node-gyp 是什么?
简单说,node-gyp 是一个“编译工具”,专门用来把 C/C++ 代码编译成 Node.js 能识别的扩展(也就是 .node 后缀的二进制文件)。node-gyp 会帮你处理这些复杂的适配工作:.node 文件,让 Node.js 可以直接 require 加载。Initialize 函数和 NODE_MODULE 注册。node-gyp 配置编译参数,执行编译命令。node-gyp 生成 .node 二进制文件。require('./addon.node') 加载,就能调用 C/C++ 实现的功能了。这样做的好处是:让 Node.js 能利用 C/C++ 的高性能(比如处理密集计算),同时保持 JavaScript 的易用性。
node-gyp 开发环境首先声明,我只用 pnpm 但是其他的一样的呐,可以自己查阅资料吧
pnpm install node-gyp node-addon-api --save-dev
node-gyp 是本地的核心编译工具
node-addon-api 简化的 C++ 接口吧,方便于寻找 node.h 的接口的呢
进行安装后在 node_module 下就会出现一个 node-gyp 的包,然后出现一个可执行文件 .bin/node-gyp
.bin 下的呐准备 C 编译环境和 python3环境进行操作
xcode-select --install 验证 xcode-select 是否进行了安装了的
brew install python3 对于liunx和macbook自带的python版本都是python2.7的,但是为了动态链接的友好性,所以尽可能使用 python3 版本吧
编写配置文件
node-gyp的是会进行识别你的配置文件的:binding.gyp这里注意下执行顺序
先进行 pnpm exec node-gyp configure ---> 生成build 目录
然后进行 pnpm exec node-gyp build --> 生成release <name>.node文件的呢
核心的使用规则是:
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)NODE_MODULE_INITIALIZER这里就通过一个简单的例子来进行讲解如何进行使用NativeAddons 吧
核心使用的技术栈是:C++ | nodejs | typescript | node-gyp ...
核心实现的是字符串加密的库吧
pnpm init -y
mkdir cpp-encrypt-addon
pnpm add -D typescript @types/node node-gyp
pnpm add node-addon-api
pnpm: 与 npm 的区别在于「依赖安装方式」—— 采用硬链接 + 符号链接管理依赖,避免重复安装,适合多项目开发
node-gyp: 编译工具,核心功能是「将 C++ 代码转为 Node.js 可识别的二进制模块(.node)」,底层依赖系统编译器(如 Windows 的 MSVC、macOS 的 Clang)
node-addon-api: 封装了 Node.js 的 N-API(C 语言接口),提供 C++ 风格的 API,避免直接写 C 语言代码
性能优势:编译型语言,直接生成机器码,比 JS(解释型)快 10-100 倍,适合加密等计算密集型任务。
系统级交互:可直接调用操作系统 API(如加密库),而 JS 受限于 V8 引擎沙箱。
STL(标准模板库):内置数据结构(字符串、容器)和算法,简化加密逻辑实现(类似前端的 Lodash,但更底层)。
命名空间(namespace):避免函数名冲突,类似前端的模块化(如namespace encrypt { ... })。
引用(&):传递变量的内存地址,避免拷贝(类似 JS 的对象引用,但类型严格)。
STL 容器与算法:
std::string:动态字符串处理(比 C 语言的 char * 更安全,自动管理内存)。
std::reverse:反转字符串(STL 算法,类似 JS 的split('').reverse().join(''))。
std::for_each:遍历容器(类似 JS 的forEach,但性能更高)。
最后的编译命令是:pnpm exec node-gyp configure build
Napi 主要是用来进行的是我们的进行 js 的数据类型和 cpp的数据类型进行转换的呐
configure:根据当前系统生成编译配置(如 Windows 生成 VS 项目,Linux 生成 Makefile)。
build:调用系统编译器(MSVC/Clang/GCC)编译 C++ 代码,生成build/Release/encrypt_addon.node。
一般在我们的`src下进行书写源代码,以及进行后续的操作吧
一般我们的 C++ 的编程包含的有:
源文件为: .c .cpp
头文件为: .h .hpp
代码的核心的编排是这些吧
定义配置文件 binding.gyp
注意事项
由于在书写 NativeAddon 的时候,我们的核心的步骤是:
napi上面的步骤就说明了这样的使用的话依赖于我们的 commonjs 规范
所以说使用的话需要使用我们的 require 函数来实现导入自定义的 addon
对于 esmodule 的话此时就需要进行对应的自定义构造得到我们的 require ,这个就是核心的 esmodule 工程化的知识了,哈哈
dts 了核心的依赖包有
开发环境依赖:
@types/node nodejs的类型包
typescript ts的核心包,内部包含有对应的 tsc 的打包工具吧
node-gyp 实现的编译NativaAddon 为 .node 文件呐
生产环境依赖
node-addon-api 核心提供的是 napi.h 这样的头文件吧 js <---> cpp官网还有很多很多的工具可以借鉴使用,自行选择最合适的搭配即可,这里呐也没有硬性要求,简单来说 NativeAddons 核心解决的还是跨平台以及性能问题的一个利器吧
- 实现突破 Javascript 的性能瓶颈
- 实现 JS 和底层系统/硬件的交互吧
- 实现复用底层的 C/C++ 生态资源
相关开源库阅读