导语:本文作者可儿,三叶草小组核心成员,爱好前端技术、古风、弓箭、手工、篆刻(此处省略好多字)人如其名,一位灰常可爱羞涩的小......小哥哥还是小姐姐文中有答案~
前言
最近发生了很多事,node终于迎来了8.1.0的更新,同时rust语言也迎来了他的1.18版本,带来了更多的更新。不过这里主要想要叙述的还是关于node的新特性napi。由于找了下网上的教程基本都止步于官网的hello_world的demo的成功运行,所以想用自己浅薄的英语知识去啃一下深入的东西。由于大部分的二进制node模块都严重依赖于V8引擎的暴露的接口,一旦v8引擎有所变动,就会导致模块的使用出现问题。所以按照程序员通用的思路,既然依赖太强,那就抽象一层吧,所以node官方做了这么一层抽象,使程序员不用直接面对V8引擎,而是使用node抽象出来的一层接口,这样保证了在各个版本之间模块的通用性等等。嗯,这段话是小生自己写的,不对之处请指出Orz。
0x00:
铁打的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了:
然后我们编辑binding.gyp:
接下来直接:
node-gyp rebuild
就可以看到本地多出了build文件夹,并且可以发现编译后的文件build/Release/hello.node,所以我们可以直接写一个js文件测试我们的demo是否成功了:
运行
node –napi-modules index.js
后发现输出world,即一切正常,我们完成了一个二进制包的编写。由于目前napi还属于新特性阶段,所以运行时需要加上--napi-modules参数。恩,大多数的文章就到此为止了,不过小生还是决定看看官方的api,深入研究下。
官网文档的研究
由于文档很长,没法一次看完慢慢整理,所以小生一边看文档,一边思索一边测试一边写,内容可能会有一点小小的混乱,请见谅。napi文档传送门
0x01:
关于文件头
由于napi是新特性,所以在运行时请加上参数--napi-modules,同时在cc文件中需加上#include <node_api.h>头部
0x02:
关于基础数据类型
-
napi_status是一个枚举数据类型,为了节省篇幅,具体定义请点击后与官网查看,该数据类型表示一个napi函数调用是否成功,每当调用一个napi的函数后,都会返回该值,表示是否操作的成功与否信息,例:
-
其中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_callback与napi_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);
0x03:
关于错误处理
所有的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;
napi_status error_code;
};
0x04:
关于异常处理
在使用napi_is_exception_pending确定有异常挂起后,有两种方式来处理异常。第一种,也是官方推荐的一种,就是简单处理后直接返回,使代码触发js内的异常,交由js自行处理。第二种,是不推荐的,就是尝试自行处理异常,可以使用napi_get_and_clear_last_exception来获取以及清除异常。不过在检索后,发现无法处理该异常,可以选择[napi_throw] []来重新抛出该异常。
相关异常处理函数:
-
napi_throw
NODE_EXTERN napi_status napi_throw(napi_env env, napi_value error);
该函数返回一个js可接受的错误
-
napi_throw_error
NODE_EXTERN napi_status napi_throw_error(napi_env env, const char* msg);
该函数抛出一个纯文本的错误信息
-
napi_throw_type_error
NODE_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg);
该函数抛出一个纯文本的类型错误信息
-
napi_throw_range_error
NODE_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg);
该函数抛出一个纯文本的RangeError
-
napi_is_error
NODE_EXTERN napi_status napi_is_error(napi_env env,napi_value value,bool* result);
该函数会检查传入的napi_value是否是一个错误对象
-
napi_create_error
NODE_EXTERN napi_status napi_create_error(napi_env env, const char* msg);
该函数会返回一个js纯文本错误
-
napi_create_type_error
NODE_EXTERN napi_status napi_create_type_error(napi_env env, const char* msg);
该函数会返回一个js纯文本类型错误
-
napi_create_range_error
NODE_EXTERN napi_status napi_create_range_error(napi_env env, const char* msg);
该函数会返回一个js纯文本RangeError
-
napi_get_and_clear_last_exception
NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env,napi_value* result);
这里官方文档应该是写错了,官方文档函数描述是This API returns true if an exception is pending.,很明显,这个描述是错的,从前文来看,该函数的效果应该是获取最近的一次的句柄,并且清除异常
-
napi_is_exception_pending
NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result);
这里官方的描述依旧是This API returns true if an exception is pending.,不过这下就正常了,该函数会在有异常被挂起时返回true
0x05:
关于生命周期
由于在napi中所有js相关的值都是一个不透明的分装,默认生命周期是和全局一致的,有时候处于安全和性能的考虑,需要将一些值得生命周期限制在一定的范围以内,此时就需要用到上文提到过得open_handle_scope和napi_close_handle_scope来创建和关闭一个上下文环境。示例:
此时,由于限制了作用域,所以每个result的生命周期都被限制在了单次循环之内。
在有些时候某些值得生命周期需要大于本身所在区域的周期时,可以将他们放在 napi_open_escapable_handle_scope与napi_close_escapable_handle_scope这两个函数之间,此间定义的值的生命周期将与父级的作用域的生命周期一致,而不局限于本身的生命周期。
相关函数签名:
关于模块注册:
Napi的模块注册使用NAPI_MODULE(addon, Init)方式来注册,其中Init方法签名如下:
void Init(napi_env env, napi_value exports, napi_value module, void* priv);
0x06:
关于js的类型值
Napi创建了一系列的api来负责创建各种类型的JavaScript值。这系列api主要用于以下功能:
-
创建一个新的JavaScript对象
-
从原始的c类型转换到N-api值
-
从N-api值转换为原始的c类型
-
获取全局实例,包括undefined和null
枚举值
-
napi_valuetype
-
napi_typedarray_type
对象构造器
-
napi_create_array
napi_status napi_create_array(napi_env env, napi_value* result)
该函数返回一个JavaScript数组类型对应的napi值
-
napi_create_array_with_length
napi_status napi_create_array_with_length(napi_env env,size_t length,napi_value* result)
该函数返回特定长度的JavaScript数组类型对应的napi值,但是该函数不能保证在创建阵列时由VM预先分配内存,如果需要保证缓冲区必须是可以通过c直接读取或写入的连续的内存,需要使用napi_create_external_arraybuffer
-
napi_create_arraybuffer
napi_status napi_create_arraybuffer(napi_env env,size_t byte_length,void* data,napi_value result)
-
napi_create_buffer
napi_status napi_create_buffer(napi_env env,size_t size,void* data,napi_value result)
-
napi_create_buffer_copy
napi_status napi_create_buffer_copy(napi_env env,size_t length,const void data,void** result_data,napi_value result)
-
napi_create_external
napi_status napi_create_external(napi_env env,void data,napi_finalize finalize_cb,void finalize_hint,napi_value* result)
-
napi_create_external_arraybuffer
napi_status napi_create_external_arraybuffer(napi_env env,void external_data,size_t byte_length,napi_finalize finalize_cb,void finalize_hint,napi_value* result)
-
napi_create_external_buffer
napi_status napi_create_external_buffer(napi_env env,size_t length,void data,napi_finalize finalize_cb,void finalize_hint,napi_value* result)
-
napi_create_function
napi_status napi_create_function(napi_env env,const char utf8name,napi_callback cb,void data,napi_value* result)
该函数返回JavaScript中函数对应的napi值,用于包装本地函数,使JavaScript可以调用它
-
napi_create_object
napi_status napi_create_object(napi_env env, napi_value* result)
该函数返回一个默认的JavaScript对象,等同于在JavaScript中调用new Object()
-
napi_create_symbol
api_status napi_create_symbol(napi_env env,const char description,napi_value result)
-
napi_create_typedarray
napi_status napi_create_typedarray(napi_env env,napi_typedarray_type type,size_t length,napi_value arraybuffer,size_t byte_offset,napi_value* result)
C->N-api值 转换函数
-
napi_create_number
napi_status napi_create_number(napi_env env, double value, napi_value* result)
-
napi_create_string_utf16
napi_status napi_create_string_utf16(napi_env env,const char16_t str,size_t length,napi_value result)
-
napi_create_string_utf8
napi_status napi_create_string_utf8(napi_env env,const char str,size_t length,napi_value result)
N-api->C 值转换函数
-
napi_get_array_length
napi_status napi_get_array_length(napi_env env,napi_value value,uint32_t* result)
-
napi_get_arraybuffer_info
napi_status napi_get_arraybuffer_info(napi_env env,napi_value arraybuffer,void* data,size_t byte_length)
-
napi_get_buffer_info
napi_status napi_get_buffer_info(napi_env env,napi_value value,void* data,size_t length)
-
napi_get_prototype
napi_status napi_get_prototype(napi_env env,napi_value object,napi_value* result)
-
napi_get_typedarray_info
napi_status napi_get_typedarray_info(napi_env env,napi_value typedarray,napi_typedarray_type type,size_t length,void* data,napi_value arraybuffer,size_t* byte_offset)
-
napi_get_value_bool
napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result)
-
napi_get_value_double
napi_status napi_get_value_double(napi_env env,napi_value value,double* result)
-
napi_get_value_external
napi_status napi_get_value_external(napi_env env,napi_value value,void** result)
-
napi_get_value_int32
napi_status napi_get_value_int32(napi_env env,napi_value value,int32_t* result)
-
napi_get_value_int64
napi_status napi_get_value_int64(napi_env env,napi_value value,int64_t* result)
-
napi_get_value_string_length
napi_status napi_get_value_string_length(napi_env env,napi_value value,int* result)
-
napi_get_value_string_utf8
napi_status napi_get_value_string_utf8(napi_env env,napi_value value,char buf,size_t bufsize,size_t result)
-
napi_get_value_string_utf16
napi_status napi_get_value_string_utf16(napi_env env,napi_value value,char16_t buf,size_t bufsize,size_t result)
-
napi_get_value_uint32
napi_status napi_get_value_uint32(napi_env env,napi_value value,uint32_t* result)
获取全局实例的函数
-
napi_get_boolean
napi_status napi_get_boolean(napi_env env, bool value, napi_value* result)
-
napi_get_global
napi_status napi_get_global(napi_env env, napi_value* result)
-
napi_get_null
napi_status napi_get_null(napi_env env, napi_value* result)
-
napi_get_undefined
napi_status napi_get_undefined(napi_env env, napi_value* result)
由于文章较长,所以分成两部分为大家呈现,明天再见啦😀
原文始发于微信公众号(三叶草小组Syclover):Node8.0之Napi探秘(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论