如何编写 Node.js 扩展

JavaScript010

如何编写 Node.js 扩展,第1张

为了创建一个Node.js扩展,我们需要编写一个继承node::ObjectWrap的C++类。 ObjectWrap 实现了让我们更容易与Javascript交互的公共方法

我们先来编写类的基本框架:

 

现在,我们必须把下面的代码编写到我们的Init()方法中:

1.声明构造函数,并将其绑定到我们的目标变量。var n = require("notification")将绑定notification() 到 n:n.notification().

1.声明属性:n.title 和n.icon.

?12345 // Set property accessors // SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle)Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon)// For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()

1.声明原型方法:n.send()

?123 // This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34) // Arguments: our constructor function, Javascript method name, C++ method name NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send)

剩下要做的就是编写我们在Init方法中用的C++方法:New,GetTitle,SetTitle,GetIcon,SetIcon,Send

构造器方法: New()

New() 方法创建了我们自定义类的新实例(一个 Gtknotify 对象),并设置一些初始值,然后返回该对象的 JavaScript 处理。这是 JavaScript 使用 new 操作符调用构造函数的期望行为。

?12345678910111213141516 std::string titlestd::string icon // new notification() static Handle<Value>New(const Arguments&args) {   HandleScope scope  Gtknotify* gtknotify_instance = new Gtknotify()  // Set some default values   gtknotify_instance->title = "Node.js"  gtknotify_instance->icon = "terminal"    // Wrap our C++ object as a Javascript object   gtknotify_instance->Wrap(args.This())    return args.This()}

getters 和 setters: GetTitle(), SetTitle(), GetIcon(), SetIcon()

下面主要是一些样板代码,可以归结为 C++ 和 JavaScript (v8) 之间的值转换。

?123456789101112131415161718192021222324 // this.title static v8::Handle<Value>GetTitle(v8::Local<v8::String>property, const v8::AccessorInfo&info) {   // Extract the C++ request object from the JavaScript wrapper.   Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder())  return v8::String::New(gtknotify_instance->title.c_str())} // this.title= static void SetTitle(Local<String>property, Local<Value>value, const AccessorInfo&info) {   Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder())  v8::String::Utf8Value v8str(value)  gtknotify_instance->title = *v8str} // this.icon static v8::Handle<Value>GetIcon(v8::Local<v8::String>property, const v8::AccessorInfo&info) {   // Extract the C++ request object from the JavaScript wrapper.   Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder())  return v8::String::New(gtknotify_instance->icon.c_str())} // this.icon= static void SetIcon(Local<String>property, Local<Value>value, const AccessorInfo&info) {   Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(info.Holder())  v8::String::Utf8Value v8str(value)  gtknotify_instance->icon = *v8str}

原型方法: Send()

首先我们抽取 C++ 对象的 this 引用,然后使用对象的属性来构建通知并显示。

?123456789101112131415161718 // this.send() static v8::Handle<Value>Send(const Arguments&args) {   v8::HandleScope scope  // Extract C++ object reference from "this"   Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap<Gtknotify>(args.This())    // Convert first argument to V8 String   v8::String::Utf8Value v8str(args[0])    // For more info on the Notify library: http://library.gnome.org/devel/libnotify/0.7/NotifyNotification.html    Notify::init("Basic")  // Arguments: title, content, icon   Notify::Notification n(gtknotify_instance->title.c_str(), *v8str, gtknotify_instance->icon.c_str())// *v8str points to the C string it wraps   // Display the notification   n.show()  // Return value   return v8::Boolean::New(true)}

编译扩展

node-waf 是一个构建工具,用来编译 Node 的扩展,这是 waf 的基本封装。构建过程可通过名为 wscript 的文件进行配置。

?1234567891011121314151617 def set_options(opt):   opt.tool_options("compiler_cxx")   def configure(conf):   conf.check_tool("compiler_cxx")   conf.check_tool("node_addon")   # This will tell the compiler to link our extension with the gtkmm and libnotifymm libraries.   conf.check_cfg(package='gtkmm-2.4', args='--cflags --libs', uselib_store='LIBGTKMM')   conf.check_cfg(package='libnotifymm-1.0', args='--cflags --libs', uselib_store='LIBNOTIFYMM')   def build(bld):   obj = bld.new_task_gen("cxx", "shlib", "node_addon")   obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"]   # This is the name of our extension.   obj.target = "gtknotify"  obj.source = "src/node_gtknotify.cpp"  obj.uselib = ['LIBGTKMM', 'LIBNOTIFYMM']

现在我们已经准备好要开始构建了,在顶级目录下运行如下命令:

?1 node-waf configure &&node-waf build

如果一切正常,我们将得到编译过的扩展,位于:./build/default/gtknotify.node ,来试试:

?123456 $ node >var notif = require('./build/default/gtknotify.node')>n = new notif.notification(){ icon: 'terminal', title: 'Node.js' } >n.send("Hello World!")true

上述的代码将在你的屏幕右上方显示一个通知信息。

打成npm包

这是非常酷的, 但是怎样与Node社区分享你的努力的成果呢? 这才是npm主要的用途: 使它更加容易扩展和分发.

打npm的扩展包是非常简单的. 你所要做的就是在你的顶级目录中创建一个包含你的扩展信息的文件package.json :

?1234567891011121314151617181920212223242526272829303132333435 {   // 扩展的名称 (不要在名称中包含node 或者 js, 这是隐式关键字).    // 这是通过require() 导入扩展的名称.   "name" : "notify",     // Version should be http://semver.org/ compliant   "version" : "v0.1.0"    // 这些脚本将在调用npm安装和npm卸载的时候运行.   , "scripts" : {       "preinstall" : "node-waf configure &&node-waf build"      , "preuninstall" : "rm -rf build/*"    }     // 这是构建我们扩展的相对路径.   , "main" : "build/default/gtknotify.node"    // 以下是可选字段:   , "description" : "Description of the extension...."  , "homepage" : "https://github.com/olalonde/node-notify"  , "author" : {       "name" : "Olivier Lalonde"      , "email" : "[email protected]"      , "url" : "http://www.syskall.com/"    }   , "repository" : {       "type" : "git"      , "url" : "https://github.com/olalonde/node-notify.git"    } }

关于package.json 格式的更多细节, 可以通过 npm help json 获取文档. 注意 大多数字段都是可选的.

skyline520

翻译于 2年前

0人顶

顶 翻译的不错哦!

你现在可以在你的顶级目录中通过运行npm install 来安装你的新的npm包了. 如果一切顺利的话, 应该可以简单的加载你的扩展 var notify = require('你的包名'). 另外一个比较有用的命令式 npm link 通过这个命令你可以创建一个到你开发目录的链接,当你的代码发生变化时不必每次都去安装/卸载.

假设你写了一个很酷的扩展, 你可能想要在中央npm库发布到网上. 首先你要先创建一个账户:

?1 $ npm adduser

下一步, 回到你的根目录编码并且运行:

?1 $ npm publish

就是这样, 你的包现在已经可以被任何人通过npm install 你的包名命令来安装了.

1、函数参数默认值

es6版本以前,不能直接为函数的参数指定默认值,只能采用变通的方法。

Es6允许可以直接设置默认参数,可以直接写在定义参数后面

相信为nodejs写过 c++扩展的人,都有过nodejs版本升级之后c++需要重新编译的惨痛经历。nodejs v8.0之后node官方推出了N-API 大大的解决了这一问题。

N-API 是独立于v8引擎之外的模块。用来向c++扩展程序提供接口,从而达到了c++扩展程序和v8引擎的隔离。因此在当nodejs版本变化之后c++扩展程序无需重新编译也能运行。

下面我们来看如何利用N-API为nodejs写一个简单的扩展:

我们的例子只从nodejs方面讲起。nodejs版本为8.x 。

首先安装 node-gyp 用来编译我们的扩展程序