Node插件开发

前言

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

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

在使用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 编译脚本

Node-API支持GYP和CMake.js两种编译方式,这里选择使用GYP方式。

新建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": {
                    # 0 - MultiThreaded (/MT)
                    # 1 - MultiThreadedDebug (/MTd)
                    # 2 - MultiThreadedDLL (/MD)
                    # 3 - MultiThreadedDebugDLL (/MDd)
                    "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文档

可以使用如下命令指定需要使用的 Visual Stuido 版本:

npm config set msvs_version 20xx

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_EXCEPTIONS

NAPI_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;

Node-API官方文档:node-addon-api doc
Node.js官方addon示例:node-addon-examples

原文:https://jiangxueqiao.com/post/819381949.html

node.js Windowv 上安装Node.js Windows 安装包(.msi) : 32 位安装包下载地址 : http://nodejs.org/dist/v0.10.26/node-v0.10.26-x86.msi 64 位安装包下载地址 : http://nodejs.org/dist/v0.10.26/x64/node-v0.10.26-x64.msi 安装步骤: 步骤 1 : 双击下载后的安装包 node-v0.10.26-x86.msi,如下所示: install-node-msi-version-on-windows-step1 步骤 2 : 点击以上的Run(运行),将出现如下界面: install-node-msi-version-on-windows-step2 步骤 3 : 勾选接受协议选项,点击 next(下一步) 按钮 : install-node-msi-version-on-windows-step3 步骤 4 : Node.js默认安装目录为 "C:\Program Files\nodejs\" , 你可以修改目录,并点击 next(下一步): install-node-msi-ve rsion-on-windows-step4 步骤 5 : 点击树形图标来选择你需要的安装模式 , 然后点击下一步 next(下一步) install-node-msi-version-on-windows-step5 步骤 6 :点击 Install(安装) 开始安装Node.js。你也可以点击 Back(返回)来修改先前的配置。 然后并点击 next(下一步): install-node-msi-version-on-windows-step6 安装过程: install-node-msi-version-on-windows-step7 点击 Finish(完成)按钮退出安装向导。 install-node-msi-version-on-windows-step8 检测PATH环境变量是否配置了Node.js,点击开始=》运行=》输入"cmd" => 输入命令"path",输出如下结果: PATH=C:\oraclexe\app\oracle\product\10.2.0\server\bin;C:\Windows\system32; C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\; c:\python32\python;C:\MinGW\bin;C:\Program Files\GTK2-Runtime\lib; C:\Program Files\MySQL\MySQL Server 5.5\bin;C:\Program Files\nodejs\; C:\Users\rg\AppData\Roaming\npm 我们可以看到环境变量中已经包含了C:\Program Files\nodejs\ 检查Node.js版本 node-version-test Windows 二进制文件 (.exe)安装 : 32 位安装包下载地址 : http://nodejs.org/dist/v0.10.26/node.exe 64 位安装包下载地址 : http://nodejs.org/dist/v0.10.26/x64/node.exe 安装步骤 步骤 1 : 双击下载的安装包 Node.exe ,将出现如下界面 : install-node-exe-on-windows-step1 点击 Run(运行)按钮将出现命令行窗口: install-node-exe-on-windows-step21 版本测试 进入 node.exe 所在的目录,如下所示: node-version 如果你获得以上输出结果,说明你已经成功安装了Node.js。 Linux上安装 Node.js Ubuntu 源码安装 以下部分我们将介绍在Ubuntu Linux下安装 Node.js 。 其他的Linux系统,如Centos等类似如下安装步骤。 在 Github 上获取 Node.js 源码: install-node-msi-version-on-linux-step1 install-node-msi-version-on-linux-step2 在完成下载后,将源码包名改为 'node'。 install-node-msi-version-on-linux-step3 修改目录权限: install-node-msi-version-on-linux-step4 使用 './configure' 创建编译文件。 install-node-msi-version-on-linux-step5 编译: make。 install-node-msi-version-on-linux-step6 完成安装: make install。 install-node-msi-version-on-linux-step7 最后我们输入'node --version' 命令来查看Node.js是否安装成功。 install-node-msi-version-on-linux-step8 Ubuntu apt-get命令安装 命令格式如下: sudo apt-get install nodejs sudo apt-get install npm centOS下安装nodejs 1、下载源码,你需要在http://nodejs.org/下载最新的Nodejs版本,本文以v0.10.24为例: cd /usr/local/src/ wget http://nodejs.org/dist/v0.10.24/node-v0.10.24.tar.gz 2、解压源码 tar zxvf node-v0.10.24.tar.gz 3、 编译安装 cd node-v0.10.24 ./configure --prefix=/usr/local/node/0.10.24 make make install 4、 配置NODE_HOME,进入profile编辑环境变量 vim /etc/profile 设置nodejs环境变量,在export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL 一行的上面添加如下内容: #set for nodejs export NODE_HOME=/usr/local/node/0.10.24 export PATH=$NODE_HOME/bin:$PATH :wq保存并退出,编译/etc/profile 使配置生效 source /etc/profile 验证是否安装配置成功 node -v 输出 v0.10.24 表示配置成功 npm模块安装路径 /usr/local/node/0.10.24/lib/node_modules/ 注:Nodejs 官网提供了编译好的Linux二进制包,你也可以下载下来直接应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值