Node插件开发(1)-快速入门

在使用Electron开发客户端时,如果现有Node模块所提供的功能无法满足需求,我们可以使用C++开发自定义的Node模块,也称插件(addon)。

Node.js插件的扩展名为.node,是二进制文件,其本质上是动态链接库重命名而来,在Windows平台是.dll文件,Linux/Unix平台是.so文件。

1. 选择Node-API

开发Node.js扩展的方式有三种:

  • Node-API(以前叫N-API)
  • nan
  • 直接使用v8、libuv等库进行开发

除非是为了使用 Node-API 未公开的接口,否则建议使用 Node-API 进行开发。

因为Node-API是二进制(ABI)兼容的,它将底层JavaScript引擎与上层插件隔离开了,JavaScripty引擎的修改不会影响我们开发的上层插件,我们基于某个版本编译的插件在不需要重新编译的情况下,就可以运行在其他版本的Node.js中。

2. 安装编译环境

Node插件使用C++开发,因此在不同的系统上采用不同的编译环境。
在Linux环境通常使用GCC和LLVM;

Mac环境通常使用Xcode;

Windows环境通常使用Visual Studio,如果不想安装完整的Visual Studio,可以使用如下命令仅安装必要的工具链:

npm install --global windows-build-tools 

Node插件通常使用node-gyp进行编译,node-gyp基于Google的gyp-next构建系统,node-gyp已经与npm捆绑在一起,但我们在使用node-gyp之前还需要先安装Python。

至此Node插件开发的环境已经搭建完成。

3.搭建工程

本文以在Windows下开发Node插件为例,其他系统环境在编译选项方面略有不同

3.1 package.json

{"name": "node-addson-sample","version": "1.0.0","private": true,"description": "A sample node addson sample","dependencies": {"bindings": "^1.5.0","node-addon-api": "^7.1.0"},"scripts": {"build-debug": "node-gyp --debug --arch=x64 configure rebuild","build-release": "node-gyp --release --arch=x64 configure rebuild","test": "node test.js"}
}

使用npm install安装依赖项。

各个依赖项的作用如下:

  • node-addon-api用于提供了Node-API相关的头文件;

  • bindings用于帮助插件开发者快速导入编译后的.node插件,方便调试,这个依赖是非必须;

build-debugbuild-release脚本分别用于编译Debug和Release版本的插件;

test脚本用于执行测试用例;

32位插件

指定arch为ia32(--arch=ia32)就可以编译32位版本的Node插件。

需要注意:64位版本Node.js只能加载64位的Node插件,32位版本的Node.js也只能加载32位的Node插件,否则会报错:

Error: \\?\D:\node-addon-sample\build\Debug\node-addon-sample.node is not a valid Win32 application.

3.2 编译脚本

新建binding.gyp文件,内容如下:

{"targets": [{"target_name": "node-addson-sample", # ***.node"cflags!": [ "-fno-exceptions" ],"cflags_cc!": [ "-fno-exceptions" ],# 指定需要编译的源文件"sources": [ "main.cpp" ],"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],# 预编译宏"defines": [ "NAPI_CPP_EXCEPTIONS", # 在Node-API中启用C++异常],"conditions": [[# Windows平台编译选项"OS == 'win'", {"configurations": {# Debug编译选项"Debug": {# 预编译宏"defines": [ "DEBUG", "_DEBUG" ],"cflags": [ "-g", "-O0" ],"conditions": [["target_arch=='x64'", {"msvs_configuration_platform": "x64",}],],"msvs_settings": {"VCCLCompilerTool": {"RuntimeLibrary": 1, # /MTd"Optimization": 0, # /Od, no optimization"MinimalRebuild": "false","OmitFramePointers": "false","BasicRuntimeChecks": 3, # /RTC1"AdditionalOptions": ["/EHsc"],},"VCLinkerTool": {"LinkIncremental": 2, # Enable incremental linking# 附加依赖库"AdditionalDependencies": [],},},# 附加包含目录"include_dirs": [],},# Debug编译选项"Release": {# 预编译宏"defines": [ "NDEBUG" ],"msvs_settings": {"VCCLCompilerTool": {"RuntimeLibrary": 0, # /MT"Optimization": 3, # /Ox, full optimization"FavorSizeOrSpeed": 1, # /Ot, favour speed over size"InlineFunctionExpansion": 2, # /Ob2, inline anything eligible"WholeProgramOptimization": "false", # Dsiable /GL, whole program optimization, needed for LTCG"OmitFramePointers": "true","EnableFunctionLevelLinking": "true","EnableIntrinsicFunctions": "true","RuntimeTypeInfo": "false","ExceptionHandling": "2", # /EHsc"AdditionalOptions": ["/MP", # compile across multiple CPUs],"DebugInformationFormat": 3,"AdditionalOptions": [],},"VCLibrarianTool": {"AdditionalOptions": ["/LTCG", # link time code generation],},"VCLinkerTool": {"LinkTimeCodeGeneration": 1, # link-time code generation"OptimizeReferences": 2, # /OPT:REF"EnableCOMDATFolding": 2, # /OPT:ICF"LinkIncremental": 1, # disable incremental linking# 附加依赖库"AdditionalDependencies": [],},},# 附加包含目录"include_dirs": [],}}},]]}]
}

binding.gyp中的编译选项大多与特定平台的编译器有关,具体可以查阅相关编译器文档,如Windows平台可以查询MSVC文档。

node-gyp官方提供了一些示例,我们可以从这些示例中获取不少灵感:

binding.gyp-files-in-the-wild

GYP官方文档:

https://gyp.gsrc.io/docs/UserDocumentation.md

3.3 第一个API

现在新建main.cpp,在该文件中定义我们的第一个API,API名为Add,支持传入2个整数参数,返回整数相加的和。

#include <napi.h>// 同步调用
// 计算两个整数相加结果并返回
Napi::Number Add(const Napi::CallbackInfo& info) {Napi::Env env = info.Env();if (info.Length() != 2)throw Napi::TypeError::New(env, "Wrong number of arguments");if (!info[0].IsNumber() || !info[1].IsNumber())throw Napi::TypeError::New(env, "Wrong arguments");const int ret = info[0].ToNumber().Int32Value() + info[1].ToNumber().Int32Value();return Napi::Number::New(env, ret);
}

在定义完API之后,还需要将API导出,在文件末尾添加如下代码:

// 导出函数
Napi::Object Init(Napi::Env env, Napi::Object exports) {exports.Set(Napi::String::New(env, "Add"), Napi::Function::New(env, Add));return exports;
}NODE_API_MODULE(addon, Init)

如果忘记导出API,加载Node插件时会报错:

Error: Module did not self-register: '\\?\D:\node-addson-sample\build\Debug\node-addson-sample.node'.

现在执行npm run build-debug编译Debug版本插件,编译生成的node插件路径为build\Debug\node-addson-sample.node

3.4 测试用例

新建test.js,测试代码如下:

const sample = require("bindings")("node-addson-sample.node");console.log(sample.Add(100, 200)); // 输出300

使用bindings模块可以不用考虑插件的具体位置,该模块会自动帮我们在项目目录下遍历查找。

4. 数据类型

napi.h头文件中声明很多继承自Napi::Value的子类,这些类分别对应JavaScript中的数据类型,如:

  • Napi::Boolean -> Boolean
  • Napi::Number -> Number
  • Napi::String -> String
  • Napi::Function -> Function
  • Napi::Symbol -> Symbol
  • Napi::Array -> Array
  • Napi::Object -> Object

Node-Api还定义Promise、Date、Buffer等数据类型。

4.1 Null和Undefined

Null和Undefined比较特殊,没有定义专门的类,需要使用Env类的成员函数返回。

env.Null()env.Undefined()

4.2 创建对象

可以通过下面两种方式创建指定类型的对象,以创建Boolean类型为例:

Napi::Boolean::New(env, true)
Napi::Value::From(env, false)

以创建一个对象数组为例介绍对象和数组的使用方法:

Napi::Array result = Napi::Array::New(env);
for (size_t i = 0; i < 3; i++) {Napi::Object obj = Napi::Object::New(env);obj.Set(Napi::String::New(env, "filePath"), Napi::String::New(env, "/root/" + std::to_string(i) + ".txt"));obj.Set(Napi::String::New(env, "fileSize"), Napi::Number::New(env, i * 100));result.Set(Napi::Number::New(env, i), obj);
}

4.3 类型校验

Napi::Value提供了若干方法用于判断当前对象是否为指定类型,如:

  • IsUndefined
  • IsNull
  • IsBoolean
  • IsNumber
  • IsString
  • IsSymbol
  • IsArray
  • IsObject
  • IsFunction
  • IsPromise
  • IsBuffer

5. 异常

可以在编译脚本binding.gyp中通过预编译宏指定是否启用C++异常:

NAPI_CPP_EXCEPTIONSNAPI_DISABLE_CPP_EXCEPTIONS

如果启用C++异常,则Napi::Error会继承自std::exception。

class Error : public ObjectReference
#ifdef NAPI_CPP_EXCEPTIONS,public std::exception
#endif  // NAPI_CPP_EXCEPTIONS
...

在启动C++异常的情况下,从Node插件抛出异常的方式如下:

throw Napi::TypeError::New(env, "Wrong number of arguments");

当前函数throw语句后面的流程将会中断执行。

TypeError继承自Error,通常用于表示与类型错误相关的异常。类似的错误类型还有RangeError等,也可以直接抛出Error类型的错误:

throw Napi::Error::New(env, "Wrong number of arguments");

在没有启动C++异常的情况下,从Node插件抛出异常的方式如下,抛出异常后需要使用return语句终止下面流程的执行:

Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
return;

更多优质内容欢迎访问我的个人站点:https://jiangxueqiao.com

Node-API官方文档:node-addon-api doc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/718701.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue的响应式企业员工绩效考评系统(源码+论文)

文章目录 前言 一、功能设计 1 普通员工功能 2 主管功能 3 系统管理员功能 4 评分标准功能 5 PC端与手机端 6 制图 二、功能实现 普通员工 1普通员工登录 2公告板块 3日志板块 主管 1主管登录 2公告板块 3日志板块 4绩效评分板块 5个人信息板块 系统管理员…

TypeScript 日期格式化工具方法

工具方法 创建工具文件&#xff1a;util.ts /*** 获取时间并格式化函数* param M 格式模板 如: YYYY-MM-DD ...* param Time 可选传入时间参数 默认为 Now*/ export const getFormatDate (M: string, Time: Date | null | string | number null) > {let date: Date Tim…

在 Linux 环境下安装 Kibana

目录 一、Kibana 是什么 二、在 Linux 环境下安装 Kibana 1、下载安装包 2、解压 3、修改 Kibana的配置文件 config/kibana.yml 4、启动 5、浏览器登录 Kibana 6、测试查询 一、Kibana 是什么 Kibana 是通向 Elastic 产品集的窗口。 它可以在 Elasticsearch 中对数据进…

品牌推广的两种飞轮:非酋飞轮与欧皇飞轮

在品牌推广的世界里&#xff0c;存在着两种截然不同的飞轮效应&#xff0c;我们称之为“非酋飞轮”与“欧皇飞轮”。这两种飞轮象征着品牌发展的两种不同路径和策略&#xff0c;而迅腾文化则以其独特的“繁”的原则&#xff0c;巧妙地将这两种飞轮结合&#xff0c;助力品牌形成…

Linux安装JumpServer并结合内网穿透实现公网访问本地服务

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Kubernetes 学习总结(46)—— Pod 不停重启问题分析与解决

我们在做性能测试的时候&#xff0c;往往会发现我们的pod服务&#xff0c;频繁重启&#xff0c;通过kubectl get pods 命令&#xff0c;我们来逐步定位问题。 现象:running的pod&#xff0c;短时间内重启次数太多。 定位问题方法:查看pod日志 kubectl get event …

【Element】实现基于 Element UI el-tabs 的左右滑动动画

实现基于 Element UI el-tabs 的左右滑动动画 引言 在构建现代 web 应用时&#xff0c;为用户提供平滑的动画效果是提升用户体验的关键。本篇博客将详细介绍如何在使用 Vue 以及 Element UI 时&#xff0c;实现一个具有左右滑动效果的 tab 切换动画。 使用 el-tabs 创建 tab…

Flutter 中的 SliverGrid 和 GridView:区别与使用场景

在 Flutter 中&#xff0c;SliverGrid 和 GridView 都是用于展示网格布局的组件&#xff0c;但它们有着不同的特点和适用场景。本文将介绍它们之间的区别以及在实际开发中的使用场景。 SliverGrid 和 GridView 的区别 SliverGrid&#xff1a; SliverGrid 是 CustomScrollView …

第十五届蓝桥杯第三期模拟赛题单

目录 第一题&#xff1a; 第二题&#xff1a; 第三题&#xff1a; 第四题: 第五题&#xff1a; 第六题&#xff1a; 第七题 第八题 第九题 第十题 第一题 【问题描述】 请问 2023 有多少个约数&#xff1f;即有多少个正整数&#xff0c;使得 2023 是这个正整数的整数倍…

FolkMQ 是怎样进行消息的事务处理?

FolkMQ 提供了二段式提交的事务提交的机制&#xff08;TCC 模型&#xff09;。允许生产者在发送消息时绑定到一个事务中并接收事务的管理&#xff0c;以确保消息的原子性&#xff08;要么全成功&#xff0c;要么全失败&#xff09;。在 FolkMQ 中&#xff0c;事务是通过 MqTran…

1、EmlogCms代码审计

一、SQL注入 1、后台标签删除处存在1处sql注入 漏洞条件 ● 漏洞url: http://emlog6.0.com/admin/tag.php?actiondell_all_tag ● 漏洞参数&#xff1a;tag[xx] ● 是否存在限制&#xff1a;无 ● 是否还有其他条件&#xff1a;actiondell_all_tag,token复现 POST /admin…

撸chatgpt3.5 api backend-api 对接wxbot

功能是实现 web 转api 对接wxbot用&#xff0c; 直接上代码&#xff0c; 1.获取wss url def get_register_websocket():# 请求头url "https://chat.openai.com/backend-api/register-websocket"payload {}headers {Authorization: Bearer eyJhbGxxxxxxxxxxxxx…

docker的网络配置

文章目录 1、网络模式1.1、bridge模式(默认模式)1.2、host模式 2、bridge模式3、自定义网络 1、网络模式 Docker在创建容器时有四种网络模式&#xff1a;bridge/host/container/none&#xff0c;bridge为默认不需要用–net去指定&#xff0c;其他三种模式需要在创建容器时使用…

【力扣 - 最长连续数组】

题目描述 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&…

Linux命令:uniq命令和wc命令

目录 1 uniq命令1.1 uniq简介1.2说明1.3案例1、默认输出2、输出重复行3、比较一行中的部分字符4、忽略大小写5、只显示唯一的行 2.4 uniq和sort命令配合使用1、文本统计2、统计IP连接数并排序 2 wc命令2.1 wc简介2.2 说明2.3 案例1、默认输出2、输出字节、字符数、单词数 总结 …

案例介绍:汽车维修系统的信息抽取技术与数据治理应用(开源)

一、引言 在当今汽车产业的快速发展中&#xff0c;软件已经成为提升车辆性能、安全性和用户体验的关键因素。从车载操作系统到智能驾驶辅助系统&#xff0c;软件技术的进步正在重塑我们对汽车的传统认知。我有幸参与了一个创新项目&#xff0c;该项目专注于开发和集成先进的汽…

关于 svg path 路径坐标 精度误差问题

<svg width"2838.739990" height"2482.179932" viewBox"0 0 2838.74 2482.18" fill"none" xmlns"http://www.w3.org/2000/svg" xmlns:xlink"http://www.w3.org/1999/xlink"><path id"矢量 12"…

原理篇-- 定时任务xxl-job-服务端(admin)项目启动过程--JobRegistryHelper 初始化 (4)

文章目录 前言一、JobRegistryHelper 作用&#xff1a;二、JobRegistryHelper 源码介绍&#xff1a;2.1 初始化start() 方法&#xff1a;2.1.1 registryOrRemoveThreadPool 执行器注册和移除&#xff1a;2.1.2 registryMonitorThread 执行器注册监控线程&#xff1a; 2.2 toSto…

折线图实现柱状阴影背景的demo

这个是一个由官网的基础折线图实现的流程&#xff0c;将涉及到的知识点附上个人浅薄的见解&#xff0c;源码在最后&#xff0c;需要的可自取。 折线图 成果展示代码注解参数backgroundColordataZoomlegendtitlexAxisyAxisgridseries 源码 成果展示 官网的基础折线图&#xff…

猫耳语音下载(mediadown)

猫耳语音下载(mediadown) 一、介绍 猫耳语音下载,能够帮助你下载猫耳音频节目。如果你是会员,它还能帮你下载会员节目。 二、下载地址 下载:猫耳语音下载(mediadown) 百度网盘下载:猫耳语音下载(mediadown) 三、安装教程 将下载的文件解压到D:\xibinhui,D:\Pr…