Node8.0 之 Napi 探秘

本文目录

  • 简介
  • Napi简介
  • 铁打的hello_world
  • 关于文件头
  • 关于基础数据类型
  • 关于错误处理
  • 关于异常处理
    • 相关异常处理函数
  • 关于生命周期
    • 相关函数
    • 关于模块注册
  • 关于js的类型值
    • 枚举值
    • 对象构造器
    • C->N-api值 转换函数
    • N-api->C 值转换函数
    • 获取全局实例的函数
    • 关于JavaScript值的抽象操作
  • 关于JavaScript中的属性
    • 官方示例
    • 索引值的demo
    • 复杂对象的demo
    • 相关结构体
    • 相关函数
  • JavaScript函数相关
  • 关于对象包裹
  • 关于异步操作

简介

最近发生了很多事,node终于迎来了8.1.0的更新,同时rust语言也迎来了他的1.18版本,带来了更多的更新。不过这里主要想要叙述的还是关于node的新特性napi。由于找了下网上的教程基本都止步于官网的hello_world的demo的成功运行,所以想用自己浅薄的英语知识去啃一下深入的东西。

Napi简介

由于大部分的二进制node模块都严重依赖于V8引擎的暴露的接口,一旦v8引擎有所变动,就会导致模块的使用出现问题。所以按照程序员通用的思路,既然依赖太强,那就抽象一层吧,所以node官方做了这么一层抽象,使程序员不用直接面对V8引擎,而是使用node抽象出来的一层接口,这样保证了在各个版本之间模块的通用性等等。嗯,这段话是小生自己写的,不对之处请指出Orz。

铁打的hello_world

讲道理,这段示例其实在各个文章中都大同小异,相信各个看官都看过好几遍了,不过为了文章的连贯性,小生还是不得不再写一次。

首先,我们更新node到8.1.0或以上版本

node --version

然后我们全局安装node-gyp,小生用的yarn,所以就用yarn安装了

yarn global add node-gyp

安装成功后我们新建一个文件夹来做实验

mkdir napi
cd napi
yarn init -y

yarn初始化项目的方式是init -y,npm是 init -f,不过这个其实没啥子,主要是初始化package.json而已。然后我们按照以前和以前写二进制模块一样的方式,先建立src目录,建立.cc文件,然后建立binding.gyp文件

mkdir src
touch src/hello.cc
touch binding.gyp

然后我们编辑hello.cc,这里直接使用官方的demo了:

#include <node_api.h>

// 实际暴露的方法,这里只是简单返回一个字符串
napi_value HelloMethod (napi_env env, napi_callback_info info) {
    napi_value world;
    napi_create_string_utf8(env, "world", 5, &world);
    return world;
}

// 扩展的初始化方法,其中 
// env:环境变量
// exports、module:node模块中对外暴露的对象
void Init (napi_env env, napi_value exports, napi_value module, void* priv) {
    // napi_property_descriptor 为结构体,作用是描述扩展暴露的 属性/方法 的描述
    napi_property_descriptor desc = { "hello", 0, HelloMethod, 0, 0, 0, napi_default, 0 };
    napi_define_properties(env, exports, 1, &desc);  // 定义暴露的方法
}

NAPI_MODULE(hello, Init);  // 注册扩展,扩展名叫做hello,Init为扩展的初始化方法复制代码

然后我们编辑binding.gyp:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "./src/hello.cc" ]
    }
  ]
}复制代码

接下来直接:

node-gyp rebuild

就可以看到本地多出了build文件夹,并且可以发现编译后的文件build/Release/hello.node,所以我们可以直接写一个js文件测试我们的demo是否成功了:

//index.js
var a=require('./build/Release/hello')
console.log(a.hello())复制代码

运行

node --napi-modules index.js

后发现输出world,即一切正常,我们完成了一个二进制包的编写。由于目前napi还属于新特性阶段,所以运行时需要加上--napi-modules参数。恩,大多数的文章就到此为止了,不过小生还是决定看看官方的api,深入研究下。

官网文档的研究

由于文档很长,没法一次看完慢慢整理,所以小生一边看文档,一边思索一边测试一边写,内容可能会有一点小小的混乱,请见谅。napi文档传送门

关于文件头

由于napi是新特性,所以在运行时请加上参数--napi-modules,同时在cc文件中需加上#include <node_api.h>头部

关于基础数据类型

  • napi_status是一个枚举数据类型,为了节省篇幅,具体定义请点击后与官网查看,该数据类型表示一个napi函数调用是否成功,每当调用一个napi的函数后,都会返回该值,表示是否操作的成功与否信息,例:
napi_value result;
napi_status status = napi_get_element(e object, i, &result);
if (status != napi_ok) {
  //do someting
}复制代码

其中napi_ok就是napi_status的枚举取值之一,表示操作成功

  • napi_extended_error_info是一个结构体,在调用函数不成功时存储了较为详细的错误信息

  • napi_env表示一个上下文的变量

  • napi_value是对所有js的基本值的一个密闭封装,简单来说,就是表示一个基本值

  • napi_handle_scope,这是一个抽象类型,用于管理和修改在特定范围内创建的对象的生命周期,使用napi_open_handle_scope将建立一个上下文环境使用napi_close_handle_scope将关闭这个上下文环境,在关闭这个上下文后,所有在其中声明的引用都将被关闭。说的简单点,就是类似于给包了一个大括弧,所有的let属性声明周期都只能在这内部。

  • napi_escapable_handle_scope是一个将特定范围内声明的值返回到父作用域的一个特殊类型

  • napi_ref是一个抽象类型,用于引用napi_value,让用户能管理js值的生命周期

  • napi_callback_info这是传递给回调函数的一个封装的数据类型,可以用于获取有关调用时的上下文信息,也可以用于设置回调函数的返回值

  • 通过Napi暴露给用户设置的回调函数指针,设置回调应该满足以下函数签名:

    typedef void (*napi_callback)(napi_env, napi_callback_info);

  • napi_async_execute_callbacknapi_async_complete_callback二者皆是与回调函数一起运行的函数指针,函数签名需要满足以下:

    typedef void (napi_async_execute_callback)(napi_env env, void data);

    typedef void (napi_async_complete_callback)(napi_env env,napi_status status,void data);

关于错误处理

所有的napi函数都使用相同的错误处理函数,在调用之后会返回napi_status,在不出错正常时会返回napi_ok,如果只是抛出异常,则会返回napi_pending_exception,在两者皆不是的情况下需要使用napi_is_exception_pending来判断是否有异常被挂起。当有错误发生时,则需要使用函数napi_get_last_error_info,该函数会将错误信息填充到参数napi_extended_error_info中,函数签名如下:

NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env,const napi_extended_error_info** result);

调用后会返回该次调用的napi_status,如果结果是napi_ok,那么result参数将会被填充成napi_extended_error_info结构体,该结构体如下:

typedef struct napi_extended_error_info {
const char error_message;
void
engine_reserved;
uint32_t engine_error_code;

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用node-gyp和ffi-napi模块让Node.js与Python进行交互,可以按照以下步骤: 1. 确保你已经安装了Node.js和Python。 2. 安装node-gyp。你可以使用npm在终端中输入以下命令来安装: ``` npm install -g node-gyp ``` 3. 创建一个Node.js项目,并在项目文件夹下执行以下命令来创建一个binding.gyp文件: ``` node-gyp configure ``` 4. 编写一个C++的扩展模块,其中包含Python的头文件和函数调用。你可以将扩展模块文件命名为example.cc。 5. 在binding.gyp文件中添加一些内容,以便将C++扩展模块编译成Node.js模块。以下是一个binding.gyp文件的示例: ``` { "targets": [ { "target_name": "example", "sources": [ "example.cc" ], "include_dirs": [ "<!(python -c \"from distutils.sysconfig import get_python_inc; print(get_python_inc())\")" ], "libraries": [ "-lpython2.7" ], "cflags": [ "-Wall", "-fPIC", "-O3" ], "cflags_cc": [ "-Wall", "-fPIC", "-O3", "-std=c++11" ] } ] } ``` 6. 使用node-gyp编译并构建你的扩展模块。在项目文件夹下执行以下命令: ``` node-gyp build ``` 7. 在Node.js代码中使用ffi-napi模块来调用C++扩展模块,以便与Python交互。以下是一个Node.js代码的示例: ``` const ffi = require('ffi-napi'); const lib = ffi.Library('./build/Release/example', { 'multiply': [ 'int', [ 'int', 'int' ] ], 'add': [ 'int', [ 'int', 'int' ] ], 'subtract': [ 'int', [ 'int', 'int' ] ] }); console.log(lib.multiply(2, 3)); console.log(lib.add(2, 3)); console.log(lib.subtract(2, 3)); ``` 在这个示例中,我们使用了multiply、add和subtract这三个函数,它们都是在C++扩展模块中定义的。这个Node.js代码将打印出6、5和-1,它们分别是multiply、add和subtract函数的返回值。 以上就是使用node-gyp和ffi-napi模块让Node.js与Python进行交互的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值