Napi实现自C++Addon转载本文请联系编程杂技公众号。Node.js的napi大大方便了c++addon的编写,让用户不再需要面对复杂的v8。本文通过一个例子来分析napi的使用以及napi的作用。1导出到js的函数#includeNAPI_MODULE(NODE_GYP_MODULE_NAME,Init)上面的代码是使用napi时的一种通用模式,我们只需要实现Init函数即可(当然也可以调用其他名称).接下来我们看一下Init的实现。napi_valueInit(napi_envenv,napi_valueexports){napi_valuefunc;//创建一个函数并将其设置为exports对象的getArray属性的值napi_create_function(env,NULL,NAPI_AUTO_LENGTH,newArray,NULL,&func);napi_set_named_property(env,exports,"getArray",func);returnexports;}napi_create_function也是napi提供的一个api。它的作用是创建一个函数。具体可以参考napi的文档。然后把这个函数导出到js中,名字是getArray。当js执行getArray时,会执行newArray函数。2newArray的实现staticnapi_valuenewArray(napi_envenv,napi_callback_infoinfo){size_targc=1;napi_valueargs[1];//获取js层的入参,这里是一个napi_get_cb_info(env,info,&argc,args,NULL,NULL);内涵;//js传入一个数字,v8转为对象,这里又将输入参数转为int类型napi_get_value_int32(env,args[0],&len);napi_valueret;//创建数组napi_create_array(env,&ret);//根据js输入参数设置数组初值for(inti=0;iisolate));returnnapi_clear_last_error(env);}我们看到napi_create_array的实现很简单,就是对v8接口的封装,然后转换成napi的类型,最后Clearerrormessages。这是Napi的典型API用法。主要包括以下1.需要传入env对象作为入参,传入一个二级指针napi_value*来保存接口的返回值。napi的返回值不是通过函数体的return返回的,return返回的是api的执行状态(成功或失败)。2处理v8的api3清除或返回错误信息。每次执行napi提供的API,如果出现错误,会通过napi_set_last_error设置为env,并返回错误码。如果不是,则通过napi_clear_last_error清除错误信息,返回napi_ok。下面看一下实现//设置当前函数调用的错误信息=code_error_code>last_error.engine_reserved=engine_reserved;returnerror_code;}//清除上次调用的错误信息env->last_error.engine_reserved=nullptr;returnnapi_ok;}调用方调用完api后,如果出现错误,调用方可以通过napi_get_last_error_info接口获取执行api的错误信息。//获取上一次调用函数的错误信息napi_statusnapi_get_last_error_info(napi_envenv,constnapi_extended_error_info**result){//初始化为非法值constintlast_status=napi_detachable_arraybuffer_expected;//根据错误码设置错误描述信息(调用结果保存)env在每次调用api中)env->last_error.error_message=error_messages[env->last_error.error_code];*result=&(env->last_error);returnnapi_ok;}言归正传,在调用napi_create_array之后,我们得到一个返回值,例如下面的ret。napi_valueret;napi_create_array(env,&ret);之前分析过,napi_value本质上是一个一级指针。然后我们看看如何使用从napi获取的数组。我们可以通过napi_set_element来设置数组的内容。//ret是数组,i是索引,num是napi_value变量,本质上是一个v8对象,即索引对应的值napi_set_element(env,ret,i,num);我们来看看napi_set_element的实现。//设置key对应的value,key是一个数字v8::Object>obj;//将napi_valueobject转化为v8对象,数组继承ObjectCHECK_TO_OBJECT(env,context,obj,object);//将valuenapi_valuevalue转化为v8对象v8::Localval=v8impl::V8LocalValueFromJsValue(value);//调用v8Object对象的Set方法设置对象的属性,即数组的元素autoset_maybe=obj->Set(context,index,val);//执行结果处理RETURN_STATUS_IF_FALSE(env,set_maybe.FromMaybe(false),napi_generic_failure);returnGET_RETURN_STATUS(env);}从上面的分析,我们大致可以看出napi实现的一些规律。getapi的逻辑是调用v8接口获取v8类型的对象,然后转换成napi_value类型返回给调用者,set的api传入napi_value类型,然后转换成v8类型目的。napi的实现几乎都在js_native_api_v8.cc中。有兴趣的同学可以看看。大多数API的实现并不复杂。理解了js_native_api_v8.cc的实现,不仅可以让我们更好的使用napi,也可以让我们更好的理解v8。用法和原理。