1、注:此教程默认你具备一定的 C/C++ 基础语法知识
2、如:指针、结构体等
3、本教程以 windows 操作系统为例,假设你会简单的使用 Visual Studio
系列文章
- C/C++ Addons 入门 Hello world!
- C/C++ Addons 对象参数及回调函数
- C/C++ Addons 非阻塞多线程回调
- C/C++ Addons windows 下 .dll 动态链接库 实用篇
前置准备
工具
-
node-gyp
- Google 出品的跨平台构建工具,初衷是用来打包 chromium 的
-
gyp
即generate your package
,将你的C/++
代码编译成node.js
可识别的文件 - 类似
webpack
将vue、jsx
等方言编译成为浏览器可识别文件 - 也可以用 cMake.js 做同样的事情
-
.node
文件在 windows 平台下既.dll
在 *nix 平台下.so
文件,.node
的尾缀只是看起来自然些
(20.0404 - 深入浅出node.js)
-
python
- 因为
node-gyp
是用python
写的 - 不止一个博客中说只能用
python2.7
- 骗人的🤬 - 官方的说明 Python v2.7, v3.5, v3.6, v3.7, or v3.8
- 因为
-
Visual Studio2017
- C/C++ 在
windows
下依赖 VS - 个人推荐 - 官方给出了木有 VS 的方案,windows-build-tools 但这没法用最重要语法提示、报错功能 - 不推荐
- C/C++ 在
姿势
目前一共有三种方式可以编写 node.js 扩展,本文以官方推荐的
N-API
为例
-
N-API
- node.js 由官方维护的
node.js
扩展 api - 纯 C 语法不依赖
node.js
版本,node.js
更新后基于N-API
写的插件照样用,官方的解释是底层调用的node.js
稳定版的二进制接口
- node.js 由官方维护的
-
node-addon-api
-
N-API
的 C++ 包装版本(有对象,更美好😝),目前 (Release 2.0.0) 并未完全的包装N-API
的所有 api
-
-
nan
-
N-API
没出来之前主要的插件开发方式 - “虽然”依赖
node.js
版本,但是维护团队很卖力,帮忙做好了每个版本的编译所以就 不依赖node.js
版本了 👍
-
-
原生 C/C++
- 极度复杂,需要用一些
v8
api、源码 - 依赖
node.js
版本,所以很难用 👎
- 极度复杂,需要用一些
起步
- 安装依赖
$ yarn add -D node-gyp # 就这一个依赖就够了
- 个人很喜欢安装到项目里面,而不是
yarn add -g node-gyp
- package.js 配置
scripts
{
"scripts": {
"configure": "node-gyp configure",
"build": "node-gyp build",
"clean": "node-gyp clean",
"rebuild": "node-gyp rebuild"
}
}
-
configure
会根据binding.gyp
在build
文件夹下生成当前平台的 C/C++ 工程 - 第一步执行这个
ps: 下面的命令干的活都交给 VS 咯,不要去折腾命令咯(除非你没有VS)
-
build
(可选) 如果你不想用 VS,只是编译已有的 C/C++ 程序,那么这条命令可以代替 VS 帮你构建 - 需要 windows-build-tools -
clean
(可选) 把build
目录清除 -
rebuild
(可选) 依次执行clean、configure、build
三条命令
- 新建 binding.gyp
{
"targets": [
{
"target_name": "hello",
"sources": [ "src/hello.c" ],
}
]
}
- 构建配置文件,语法同 js 版本的 json。等价于 webpack.config.js
-
targets
下面的每一项都可以理解为一个node插件
,等价于webpack
打包bundle.js
-
target_name
即require([target_name])
-
sources
C/C++ 源码目录 - 更多配置参考
- 生成目标平台项目
$ yarn configure
- 启动
Visual Studio
编写扩展
- 一些 API 说明
napi_status 枚举
· 调用任意 N-API 后返回值类型
napi_extended_error_info 结构体
· 表示调用 N-API 后发生的错误对象
napi_env 结构体
· 告诉 N-API 当前执行上下文,在调用 Addons 时自动(Init)传入
· 调用任意多个、或嵌套 N-API 时候需要一直传递下去,不允许重用
napi_callback_info
· 用于 Addons 获取 js 调用时候传入的上下文信息,如参数
napi_value 不透明指针
· N-API 提供的在 C、js 中间的一种数据类型
· 任意的 js 数据类型都可以赋值给 napi_value,然后通过 N-API 提供的方法再把 napi_value 转成 C 语言的类型,反之亦然
napi_threadsafe_function 不透明指针
· 代表一个 js 的 function,在多线程模式下通过 napi_call_threadsafe_function 调用实现异步 😁
# 函数
napi_create_string_utf8
· 创建 napi 类型的 string
· 相当于 const str = 'Hello world!'
napi_get_property
· 从 napi 类型的对象中取值
· 相当于对 json = { name: 'anan', age: 29 } 取值: console.log(json.name, json.age)
napi_get_cb_info
· **** 这个可以说是最重要的 API 了,再怎么强调也不为过 ****
· 用于获取 js 的入参;如原始值类型、对象类型,甚至拿到 function 类型的指针地址
· 可以说是 js 和 N-NAP 之前的桥梁
napi_call_function
· Addons 调用 js 回调
napi_create_function
· 创建 js 函数
napi_get_global
· 在 Addons 中获取 js 的 global 对象
- C/C++
src/hello.c
#include <stdio.h>
#include <node_api.h>
#include <string.h>
napi_value Hello(napi_env env, napi_callback_info info) {
size_t argc = 1; // 只接受一个参数
napi_value argv; // 接收到的参数
char n[40];
char hello[90] = "Hello ";
napi_value result;
napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); // 获取接收参数
napi_get_value_string_utf8(env, argv, n, sizeof(n), NULL); // 将接收到的参数转换为 C 语言类型
napi_create_string_utf8(env, strcat(hello, n), NAPI_AUTO_LENGTH, &result); // 拼接字符串
return result;
}
napi_value Init(napi_env env, napi_value exports) {
// 描述 hello 属性
napi_property_descriptor desc = {
"hello",
NULL,
Hello,
NULL,
NULL,
NULL,
napi_default,
NULL };
// 将 hello 挂载到 exports 上面 === require('hello.node').hello;
napi_define_properties(env, exports, 1, &desc);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
ps: 编写好C代码后,Ctrl+Shift+b (VS编译快捷键)
- javascript
test/hello.js
// const addon = require('./build/Debug/hello.node'); // 如果 VS 编译模式是 Debug
const addon = require('./build/Release/hello.node'); // 如果 VS 编译模式是 Release
console.log(addon.hello('world!'));
- 运行
$ node test/hello.js
Hello world!
Boom Shakalaka