N-API模块的编写与发布
前言
作为nodejs的底层开发语言,c++具有高性能、复用性好等优势,c++编写的chrome v8引擎与libuv、http-parser、zlib等等一起构成了现在大前端技术的基础——nodejs。
nodejs也提供了编写c++扩展以提高性能的方法。但是原生 C++ 模块的开发一直是一个被人冷落的角落,其中一部分原因在于Node程序员有一部分来自于前端,c++的学习成本很高可能没有系统的C++知识,另一部分原因是node的c++扩展机制很复杂,需要理解v8的各种概念,包括隔离示例、虚拟机、上下文、句柄、内存回收(gc)、模板等等。更难过的是,随着版本的更替,一个编译好的插件发布没多久,就会因为版本的不兼容不得不重新编译以适应版本。
关于C++插件开发的发展史,可以参考以下这篇文章
从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁
N-API简介
N-API是node8.3新增的实验性特性,在node10中正式启用,下面是官网的说明
N-API (pronounced N as in the letter, followed by API) is an API for building native Addons. It is independent from the underlying JavaScript runtime (ex V8) and is maintained as part of Node.js itself. This API will be Application Binary Interface (ABI) stable across versions of Node.js. It is intended to insulate Addons from changes in the underlying JavaScript engine and allow modules compiled for one version to run on later versions of Node.js without recompilation.Addons are built/packaged with the same approach/tools outlined in the section titled C++ Addons. The only difference is the set of APIs that are used by the native code. Instead of using the V8 or Native Abstractions for Node.js APIs, the functions available in the N-API are used.
N -API是一个构建本地扩展插件的API。它并不依赖于JavaScript运行环境(例如v8)而是作为Node.js自身的组建的一部分运行。在Node.js当中这个API将会作为应用程序二进制接口(ABI) 稳定存在。它的目的是为了可以让插件直接与JS引擎底层交互并允许它们编译一次之后无需为每个新版本的Node重新编译。
这个插件的构建、打包和使用与一般C++插件相同。惟一的不同的是这组API调用Node.js原生对象,而不是使用v8或者本地抽象的Node.js API。
相关准备
以下均为win环境
方法一:
按先后顺序全局安装 windows-build-tools、node-gyp 和 node-pre-gyp-github
npm install --global --production windows-build-tools
npm install -g node-gyp
npm install -g node-pre-gyp-github
windows-build-tools
windows环境下,node-gyp工作必须工具
node-gyp
c++扩展编译工具
node-pre-gyp-github
二进制包分发工具node-pre-gyp在github的实现
解决部分使用环境下缺少c++编译工具的问题,例如使用tag以slim和alpine结尾的node镜像运行的容器中
安装完成之后需要设置相关环境变量,具体参照node-pre-gyp-github
方法二:
安装docker for windows,具体安装方法自行参考官网
编写
n-api扩展模块中代码的编写可以参考官方中文文档和官方示例(node10),binding.gyp 文件的编写可以参考这个页面所列出来的模块的binding.gyp。官方示例为使用 node_api.h头文件,在最新的node12预览版中,可以使用新增的 node_api.h 所引用的 js_native_api.h 头文件。与 node_api.h 相比 使用 js_native_api.h 编写的c++扩展无法使用nodejs中的一些特性,目前发现无法直接调用的功能包括Buffer对象、获取任务队列、异步操作,意图将js标准api与node特定api隔离,降低耦合性,具体可以参考这个PR。
这是我所写的一个例子,传送门:https://github.com/zhouzhi3859/napi_example
克隆下来之后,在文件目录运行 npm install 之后会看见除了node_modules与package-lock.json之外,还多出来binding文件夹,里面存放的正是直接从github上下载的 .node 结尾的文件,不需要本地编译。
发布
以下命令运行均运行于当前工程目录下
上面第一种环境中,编译发布针对win:
npm run build
npm run package
npm run publish
上面第二种环境中:
docker pull node // 不能用tag以slim与alpine结尾的镜像,这些镜像缺少c++扩展编译环境
docker run -it -v $pwd:/root --name napi_example_build -w /root node bash // 进入容器中
容器中执行以下命令
npm run build
npm run package
npm run publish
然后退出容器
接下来就可以按照正常的npm模块发布流程发布所编写的c++扩展了