返回顶部
首页 > 资讯 > 前端开发 > node.js >Node.js插件的正确编写方式
  • 796
分享到

Node.js插件的正确编写方式

插件正确方式 2022-06-04 01:06:09 796人浏览 薄情痞子
摘要

node.js在利用javascript编写后端方面效果拔群,值得我们多加尝试。不过如果大家需要一些无法直接使用的功能甚至是根本无从实现的模块使用,那么能否从C/C++库当中引入此类成果呢?答案是肯定的,大

node.js在利用javascript编写后端方面效果拔群,值得我们多加尝试。不过如果大家需要一些无法直接使用的功能甚至是根本无从实现的模块使用,那么能否从C/C++库当中引入此类成果呢?答案是肯定的,大家要做的就是编写一款插件,并借此在自己的JavaScript代码中使用其它代码库的资源。下面我们就一同开始今天的探询之旅。

介绍

正如node.js在官方说明文档中所言,插件是以动态方式进行链接的共享式对象,能够将JavaScript代码与C/c++库接驳起来。这意味着我们可以引用任何来自C/C++库中的内容,并通过创建插件的方式将其纳入到Node.js当中。

作为实例,我们将为标准std::string对象创建一套封装。

准备工作

在我们开始编写工作之前,大家首先需要确保自己已经准备好所有后续模块编译所需要的素材。大家需要node-gyp及其全部依赖关系。大家可以利用以下命令安装node-gyp:


npm install -g node-gyp

在依赖性方面,我们需要为Unix系统准备以下项目:• python (要求2.7版本, 3.x无法正常起效)

• make

• 一款C++编译器工具链(例如gpp或者g++)

举例来说,在ubuntu上大家可以利用以下命令安装所有上述项目(其中Python 2.7应该已经预先安装完毕了):


sudo apt-get install build-essentials 

windows系统环境下,大家需要的是:

• Python (2.7.3版本, 3.x无法正常起效)

• 微软Visual Studio C++ 2010 (适用于Windows XP/Vista)

• 微软Visual Studio C++ 2012 for Windows Desktop (适用于Windows 7/8)

强调一点,Visual Studio的Express版本也能正常起效。

binding.gyp文件

该文件由node-gyp使用,旨在为我们的插件生成适当的build文件。大家可以点击此处查看维基百科提供的.gyp文件说明文档,但今天我们要使用的实例非常简单、因此只需使用以下代码即可:


{ 
  "targets": [ 
    { 
      "target_name": "stdstring", 
      "sources": [ "addon.cc", "stdstring.cc" ] 
    } 
  ] 
}

其中target_name可以设置为大家喜欢的任何内容。而sources数组当中包含该插件需要用到的所有源文件。在我们的实例中还包括addon.cc,它的作用在于容纳编译插件及stdstring.cc所必需的代码,外加我们的封装类。

STDStringWrapper类

第一步,我们要做的是在stdstring.h文件当中定义自己的类。如果大家对于C++编程比较熟悉,那么也一定不会对以下两行代码感到陌生。


#ifndef STDSTRING_H 
#define STDSTRING_H

这属于标准的include guard。接下来,我们需要将以下两个header纳入include范畴:

#include
#include
第一个面向的是std::string类,而第二个include则作用于全部与Node以及V8相关的内容。

这一步完成之后,我们可以对自己的类进行声明:

class STDStringWrapper : public node::ObjectWrap {
对于所有我们打算包含在插件当中的类来说,我们必须扩展node::ObjectWrap类。

现在我们可以开始定义该类的private属性了:


private: 
  std::string* s_; 
  
  explicit STDStringWrapper(std::string s = ""); 
  ~STDStringWrapper();

除了构造函数与解析函数,我们还需要为std::string定义一个指针。这是该技术的核心所在,能够被用于将C/C++代码库与Node相对接——我们为该C/C++类定义一个私有指针,并将在随后的所有方法中利用该指针实现操作。

现在我们声明的constructor静态属性,它将为我们在V8中创建的类提供函数:

static v8::Persistent constructor;
感兴趣的朋友可以点击此处参阅模板说明方案以获取更多细节信息。

现在我们还需要一个New方法,它将被分配给前面提到的constructor,同时V8会对我们的类进行初始化:

static v8::Handle New(const v8::Arguments& args);
作用于V8的每一个函数都应该遵循以下要求:它将接受指向v8::Arguments对象的引用,并返回一个v8::Handle>v8::Value>——这正是我们在使用强类型C++编码时,V8处理弱类型JavaScript的一贯方式。

在此之后,我们还需要将另外两个方法插入到对象的原型当中:


static v8::Handle add(const v8::Arguments& args); 
static v8::Handle toString(const v8::Arguments& args);

其中toString()方法允许我们在将其与普通JavaScript字符串共同使用时获得s_的值而非[Object object]的值。

最后,我们将引入初始化方法(此方法将由V8调用并指派给constructor函数)并关闭include guard:


public: 
    static void Init(v8::Handle exports); 
}; 
  
#endif

其中exports对象在JavaScript模块中的作用等同于module.exports。

stdstring.cc文件、构造函数与解析函数

现在来创建stdstring.cc文件。我们首先需要include我们的header:


#include "stdstring.h" 

下面为constructor定义属性(因为它属于静态函数):


v8::Persistent STDStringWrapper::constructor; 

这个为类服务的构造函数将分配s_属性:


STDStringWrapper::STDStringWrapper(std::string s) { 
  s_ = new std::string(s); 
}

而解析函数将对其进行delete,从而避免内存溢出:


STDStringWrapper::~STDStringWrapper() { 
  delete s_; 
}

再有,大家必须delete掉所有与new一同分配的内容,因为每一次此类情况都有可能造成异常,因此请牢牢记住上述操作或者使用共享指针。

Init方法

该方法将由V8加以调用,旨在对我们的类进行初始化(分配constructor,将我们所有打算在JavaScript当中使用的内容安置在exports对象当中):

void STDStringWrapper::Init(v8::Handle exports) {
首先,我们需要为自己的New方法创建一个函数模板:

v8::Local tpl = v8::FunctionTemplate::New(New);
这有点类似于JavaScipt当中的new Function——它允许我们准备好自己的JavaScript类。

现在我们可以根据实际需要为该函数设定名称了(如果大家漏掉了这一步,那么构造函数将处于匿名状态,即名称为function someName() {}或者function () {}):

tpl->SetClassName(v8::String::NewSymbol("STDString"));
我们利用v8::String::NewSymbol()来创建一个用于属性名称的特殊类型字符串——这能为引擎的运作节约一点点时间。

在此之后,我们需要设定我们的类实例当中包含多少个字段:

tpl->InstanceTemplate()->SetInternalFieldCount(2);
我们拥有两个方法——add()与toString(),因此我们将数量设置为2。现在我们可以将自己的方法添加到函数原型当中了:

tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("add"), v8::FunctionTemplate::New(add)->GetFunction());
tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("toString"), v8::FunctionTemplate::New(toString)->GetFunction());
这部分代码量看起来比较大,但只要认真观察大家就会发现其中的规律:我们利用tpl->PrototypeTemplate()->Set()来添加每一个方法。我们还利用v8::String::NewSymbol()为它们提供名称与FunctionTemplate。

最后,我们可以将该构造函数安置于我们的constructor类属性内的exports对象中:


constructor = v8::Persistent::New(tpl->GetFunction()); 
  exports->Set(v8::String::NewSymbol("STDString"), constructor); 
}

New方法

现在我们要做的是定义一个与JavaScript Object.prototype.constructor运作效果相同的方法:


v8::Handle STDStringWrapper::New(const v8::Arguments& args) {

我们首先需要为其创建一个范围:


v8::HandleScope scope; 

在此之后,我们可以利用args对象的.IsConstructCall()方法来检查该构造函数是否能够利用new关键词加以调用:


if (args.IsConstructCall()) { 

如果可以,我们首先如下所示将参数传递至std::string处:


v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str);

……这样我们就能将它传递到我们封装类的构造函数当中了:


STDStringWrapper* obj = new STDStringWrapper(s);

在此之后,我们可以利用之前创建的该对象的.Wrap()方法(继承自node::ObjectWrap)来将它分配给this变量:


obj->Wrap(args.This()); 

最后,我们可以返回这个新创建的对象:


return args.This(); 

如果该函数无法利用new进行调用,我们也可以直接调用构造函数。接下来,我们要做的是为参数计数设置一个常数:


} else { 
  const int arGC = 1; 

现在我们需要利用自己的参数创建一个数组:


v8::Local argv[argc] = { args[0] }; 

然后将constructor->NewInstance方法的结果传递至scope.Close,这样该对象就能在随后发挥作用(scope.Close基本上允许大家通过将对象处理句柄移动至更高范围的方式对其加以维持——这也是函数的起效方式):


    return scope.Close(constructor->NewInstance(argc, argv)); 
  } 
} 

add方法

现在让我们创建add方法,它的作用是允许大家向对象的内部std::string添加内容:


v8::Handle STDStringWrapper::add(const v8::Arguments& args) { 

首先,我们需要为我们的函数创建一个范围,并像之前那样把该参数转换到std::string当中:


v8::HandleScope scope; 
  
v8::String::Utf8Value str(args[0]->ToString()); 
std::string s(*str); 

现在我们需要对该对象进行拆包。我们之前也进行过这种反向封装操作——这一次我们是要从this变量当中获取指向对象的指针。


STDStringWrapper* obj = ObjectWrap::Unwrap(args.This()); 

接着我们可以访问s_属性并使用其.append()方法:


obj->s_->append(s); 

最后,我们返回s_属性的当前值(需要再次使用scope.Close):


return scope.Close(v8::String::New(obj->s_->c_str())); 

由于v8::String::New()方法只能将char pointer作为值来接受,因此我们需要使用obj->s_->c_str()来加以获取。

这时大家的插件文件夹中还应该创建出一个build目录。

测试

现在我们可以对自己的插件进行测试了。在我们的插件目录中创建一个test.js文件以及必要的编译库(大家可以直接略过.node扩展):


var addon = require('./build/Release/addon'); 

下一步,为我们的对象创建一个新实例:


var test = new addon.STDString('test'); 

下面再对其进行操作,例如添加或者将其转化为字符串:


test.add('!'); 
console.log('test's contents: %s', test); 

在运行之后,大家应该在控制台中看到以下执行结果:

结论

我希望大家能在阅读了本教程之后打消顾虑,将创建与测试以C/C++库为基础的定制化Node.js插件视为一项无甚难度的任务。大家可以利用这种技术轻松将几乎任何C/C++库引入Node.js当中。如果大家愿意,还可以根据实际需求为插件添加更多功能。std::string当中提供大量方法,我们可以将它们作为练习素材。

实用链接

感兴趣的朋友可以查看以下链接以获取更多与Node.js插件开发、V8以及C事件循环库相关的资源与详细信息。

• Node.js插件说明文档

• V8说明文档

• libuv (C事件循环库),来自GitHub

英文:Http://code.tutsplus.com/tutorials/writing-nodejs-addons--cms-21771

--结束END--

本文标题: Node.js插件的正确编写方式

本文链接: https://lsjlt.com/news/11675.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Node.js插件的正确编写方式
    Node.js在利用JavaScript编写后端方面效果拔群,值得我们多加尝试。不过如果大家需要一些无法直接使用的功能甚至是根本无从实现的模块使用,那么能否从C/C++库当中引入此类成果呢?答案是肯定的,大...
    99+
    2022-06-04
    插件 正确 方式
  • golang函数文档编写的正确打开方式
    golang 函数文档应包含函数签名、功能描述、输入参数、输出值和示例。编写函数文档的最佳实践包括使用 godoc 注释、保持简短简洁、提供具体示例、使用代码块和持续更新。 Golan...
    99+
    2024-04-28
    golang 函数文档
  • Node.js编写组件的三种实现方式
    首先介绍使用v8 API跟使用swig框架的不同: (1)v8 API方式为官方提供的原生方法,功能强大而完善,缺点是需要熟悉v8 API,编写起来比较麻烦,是js强相关的,不容易支持其它脚本语言。 (2)...
    99+
    2022-06-04
    三种 组件 方式
  • 如何正确编写PHP方法体
    如何正确编写PHP方法体,需要具体代码示例 在PHP编程中,方法(或者称为函数)是一段封装了一些指定任务或逻辑的代码块,通过调用方法可以重复使用这段代码,提高代码的重用性和可维护性。编...
    99+
    2024-04-02
  • SpringBoot下载文件的正确方式~
    Spring Boot 配合 axios 实现文件下载功能 前言比较一般的解法分析原因 正确解法 前言 最近遇到一个奇怪的需求,前端通过post请求下载压缩文件,同时会传给后端一些数据,用于生成压缩包。此时后端接口就不仅仅是...
    99+
    2023-08-23
    spring boot java spring
  • 三种Node.js写文件的方式
    本文分享了Node.js写文件的三种方式,具体内容和如下 1、通过管道流写文件   采用管道传输二进制流,可以实现自动管理流,可写流不必当心可读流流的过快而崩溃,适合大小文件传输(推荐) var re...
    99+
    2022-06-04
    三种 方式 文件
  • Golang中正确的文件删除方式
    在日常编程工作中,处理文件是一个常见的操作,而删除文件也是经常会用到的功能之一。在Golang中,删除文件同样是一个常见的操作,但是需要一些注意事项和最佳实践方案来确保操作的安全和正确...
    99+
    2024-02-26
    最佳方法 文件删除 golang实践
  • Go语言文件关闭的正确方式
    标题:Go语言文件关闭的正确方式 在Go语言中,文件操作是非常常见的操作之一,然而在处理文件时正确关闭文件是非常重要的,否则可能会导致资源泄露或者文件操作异常。本文将介绍在Go语言中关...
    99+
    2024-02-28
    go语言 关闭文件 正确方式
  • 报表查询条件的正确打开方式
    众所周知,大拿们在设计报表的时候,需要展现的数据很少是固定不变地,经常需要使用查询条件过滤出我们想要的数据。简而言之就是,同一张报表可以在不同的参数控制下呈现出不同的数据。那么问题来了,报表工具一般都用哪些方法来实现这一需求呢?使用SQL参...
    99+
    2023-06-02
  • MySQL中MVCC的正确打开方式
     最近在学习MySQL中的MVCC,看了网上的各种版本,什么创建版本号、删除版本号,一开始看的时候,好像很对的样子,但实际上很多都是错误的。经过好几天的查阅对比,在几篇博客的帮助下,才算是觉得正确理解了MySQL中的MVCC。       ...
    99+
    2021-07-01
    MySQL中MVCC的正确打开方式
  • React 正确使用useCallback useMemo的方式
    目录正确使用useCallback useMemo的姿势useCallbackuseMemo总结正确使用useCallback useMemo的姿势 说起useCallback us...
    99+
    2022-11-13
    React 使用useCallback useMemo方式 React  useCallback useMemo
  • PHP符号的正确使用方式
    PHP符号的正确使用方式,需要具体代码示例 PHP是一种流行的服务器端脚本语言,广泛用于Web开发。在PHP中,符号的正确使用方式是非常重要的,它能够决定代码的执行逻辑和结果的准确性。...
    99+
    2024-03-15
    函数 变量 字符串
  • golang函数测试的正确方式
    在 go 语言中进行函数测试的正确方式包括:创建以 _test.go 结尾的测试文件。编写以 test 开头的测试函数。使用 t.fail()、t.error()、t.fatal() 和...
    99+
    2024-04-27
    测试 golang
  • Redis分布式锁的正确实现方式
    Redis分布式锁的正确实现方式?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。可靠性首先,为了确保分布式锁可用,我们至少要确保...
    99+
    2024-04-02
  • Vue编写炫酷的时钟插件
    本文实例为大家分享了Vue编写时钟插件的具体代码,供大家参考,具体内容如下 效果图 代码奉上: <template>     <div class="clock"...
    99+
    2024-04-02
  • nodejs 子进程正确的打开方式
    因为库太拙了,需要在 nodejs 里调用子进程来获取数据。然而看到 child_process 的文档真是头疼,这么多种启动子进程的方法直接推到人面前,也没个解释,命名也十分无用。只能一个个地查看详细说明...
    99+
    2022-06-04
    进程 正确 方式
  • setTimeout在vue中的正确使用方式
    目录setTimeout在vue中的正确使用遇到了1个坑问题出在我的格式上面解决vue在setTimeout内修改this失效使用箭头函数总结setTimeout在vue中的正确使用...
    99+
    2023-01-28
    vue使用setTimeout vue setTimeout setTimeout在vue使用
  • node.js读写json文件的方法
    本文实例为大家分享了node.js读写json文件的具体代码,供大家参考,具体内容如下 一、引入依赖包 const fs = require('fs') const path = r...
    99+
    2024-04-02
  • Node.js 原生插件:如何编写、调试和部署自定义模块
    Node.js 原生插件是一种特殊的模块,它可以通过 C/C++ 语言编写,并在项目中使用。由于原生插件是直接通过 C/C++ 语言编写的,因此它的执行速度比纯 JavaScript 模块更快。此外,原生插件还可以访问一些 JavaSc...
    99+
    2024-02-25
    Node.js 插件 Node.js 原生插件 Node.js 模块 Node.js 自定义模块 Node.js 插件编写 Node.js 插件调试 Node.js 插件部署
  • ProxyWidget和Element更新的正确方式详解
    目录正文1. ProxyWidget和ProxyElement的主要功能2. InheritedWidget2.1 注册2.2 通知2.3 何时更新?3. ParentDataWid...
    99+
    2023-01-18
    ProxyWidget Element更新 ProxyWidget Element
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作