Napi是node.js官方给出的接口,用于写c、c++扩展。2017年中推出。
本文旨在帮助(像我一样的)新手朋友迈出第一步。使用了node-addon-api。
系统:windows10 64bit
一、准备开发环境
1、安装好node.js,可以node 和 npm。比较简单就不说了,结果如下图。可以用cmd或git bash依个人喜好。
2、新建文件夹
可以建在任何喜欢的地方。我是为了用vs的intellisense(不接受吐槽),所以用vs新建了一个空项目,文件夹位置为D:\projects\plugin4js\plugin4js。我们以下的命令都在此路径下。
使用intellisense的方法放在最后
。
3、初始化
npm init -f
-f意为force,会强制填充默认值。否则会让你手动输入很多参数。不用担心,这些参数可以稍后修改。
init 之后会出现一个package.json文件。其中”name”项即为你文件夹的名称,也就是你项目的名称。
4、安装依赖
一共3个东西,都用npm安装:node-gyp,bindings和node-addon-api
首先安装node-gyp。这是编译需要的。安装它有一点麻烦,因为需要python2和其他依赖项。如果你没有,请用
管理员权限
打开git bash或cmd:
npm install --global --production windows-build-tools
–global为全局安装。成功后继续:
npm install --global node-gyp
设置python环境变量:
setx PYTHON "%USERPROFILE%\.windows-build-tools\python27\python.exe"
然后安装:
npm install node-addon-api --save
npm install bindings --save
–save命令会把依赖关系写进package.json的”dependencies”项。
二、写源文件
新建源文件plugin4js.cpp。
#include <napi.h>
Napi::String Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::String str= Napi::String::New(env, "hello "+info[0].As<Napi::String>().Utf8Value());
return str;
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "hello"),
Napi::Function::New(env, Method));
return exports;
}
NODE_API_MODULE(hello,Init)
传入参数 info为JS脚本中传入的参数,可能有多个。可以用info[0],info[1]…获取。
env顾名思义就是环境。很多情况下都需要传入,我们看到node-addon-api接口已经把它放进了info中,可以用info.Env()获取。
第5行使用Napi提供的New方法新建了一个Napi::String类型的变量。它把字符串”hello”和JS脚本传入的参数进行拼接,形成新字符串。
Init函数使用exports导出函数,函数名为”hello”。
最后一行导出模块。第一个参数为模块名。
三、编译
1.修改package.json文件。
{
"name": "plugin4js",
"version": "1.0.0",
"description": "A c++ plugin for js",
"main": "index.js",
"scripts": {
"test": "node test.js",
},
"keywords": ["plugin"],
"author": "TOMATO",
"license": "ISC",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "^1.6.3"
},
"gypfile": true
}
其实没什么好改的,”description”、”main”、”keywords”、”author”都是依个人喜好。
“scripts”可以定义脚本命令。如”test”一行可以改为”test”: “node test.js”,则可以输入npm run test命令进行测试,相当于输入了”node test.js”命令。也可以添加其他随你喜欢的命令。
最
重要
的是要添加这一项:”gypfile”: true。它告诉程序使用node-gyp编译,并寻找binding.gyp文件。
2.编写binding.gyp。
新建binding.gyp文件:
{
"targets":
[
{
"target_name":"hello",
"sources":["plugin4js.cpp"],
"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']
}
]
}
“target_name”为编译目标名称,即生成.node文件的名称;
“sources”:源文件。
“include_dirs”、”dependencies”为包含目录、依赖目录。这里是自动获得地址,填写全路径也可以。多个路径用”,”隔开。
“cflags!”、”cflags_cc!”是消除警告。
到这里我还是编译报错,如下图:
于是加了最后一行’defines’:[‘NAPI_DISABLE_CPP_EXCEPTIONS’]。
现在可以编译成功了,命令如下:
node-gyp configure rebuild
出现gyp info ok字样表示成功。
四、测试
新建测试脚本test.js:
const hello = require("bindings")("hello");
console.log(hello.hello("TOMATO"));
require导入模块,bindings自动查找模块文件。编译好的hello.node文件在./build/Release/中。
hello函数传入的参数内容可以随意更改,但应为字符串类型。
命令行运行:node test.js即可看到输出。因为在package.json文件在我们定义了脚本命令test,所以也可以使用命令:npm run test达到相同目的。
恭喜你看到输出结果了!好啦,至此你已经勇敢迈出了第一步。
TIPS:我没有忘记:使用intellisense的方法:
其实很简单,添加包含目录就可以了。属性管理器->通用属性->VC++目录-包含目录,添加:D:\projects\vision_cpp4js\vision_cpp4js\node_modules\node-addon-api和D:\projects\vision_cpp4js\vision_cpp4js\node_modules\node-addon-api\src。