为JS写C++扩展,Napi第一步

  • Post author:
  • Post category:其他


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。



版权声明:本文为tomato3351原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。