如何编写js文件 Node.js 扩展

使用Node.js为其他程序编写扩展的基本方法
首先我们用下面的目录结构来创建一个节点通知(node-notify)文件夹.
|-- build/&&&&&&&&&&&&&&&&&& # This is where our extension is built.
|&& `-- demo.js&&&&&&&&&&&&& # This is a demo Node.js script to test our extension.
|&& `-- node_gtknotify.cpp&& # This is the where we do the mapping from C++ to Javascript.
`-- wscript&&&&&&&&&&&&&&&&& # This is our build configuration used by node-waf
这个看起来很漂亮的tree 用通用的 tree 生成.
现在让我来创建测试脚本demo.js 和决定我们扩展的API前期看起来应该像:
var notify = require(&../build/default/gtknotify.node&);
var notification = new notify.notification();
notification.title = &Notification title&;
notification.icon = &emblem-default&;
notification.send(&Notification message&);
编写我们的Node.js扩展
为了创建一个Node.js扩展,我们需要编写一个继承node::ObjectWrap的C++类。 ObjectWrap 实现了让我们更容易与Javascript交互的公共方法
我们先来编写类的基本框架:
#include &v8.h& // v8 is the Javascript engine used by QNode
#include &node.h&
#include &string&
#include &gtkmm.h&
#include &libnotifymm.h&
using namespace v8;
class Gtknotify : node::ObjectWrap {
&&Gtknotify() {}
&&~Gtknotify() {}
&&static void Init(Handle&Object& target) {
v8::Persistent&FunctionTemplate& Gtknotify::persistent_function_
extern &C& {
&static void init(Handle&Object& target) {
&&Gtknotify::Init(target);
&NODE_MODULE(gtknotify, init);
现在,我们必须把下面的代码编写到我们的Init()方法中:
&&& 声明构造函数,并将其绑定到我们的目标变量。var n = require(&notification&);将绑定notification() 到 n:n.notification().
&&v8::Local&FunctionTemplate& local_function_template = v8::FunctionTemplate::New(New);
&&Gtknotify::persistent_function_template = v8::Persistent&FunctionTemplate&::New(local_function_template);
&&Gtknotify::persistent_function_template-&InstanceTemplate()-&SetInternalFieldCount(1);
&&Gtknotify::persistent_function_template-&SetClassName(v8::String::NewSymbol(&Notification&));
&&target-&Set(String::NewSymbol(&notification&), Gtknotify::persistent_function_template-&GetFunction());
&&& 声明属性:n.title 和n.icon.
Gtknotify::persistent_function_template-&InstanceTemplate()-&SetAccessor(String::New(&title&), GetTitle, SetTitle);
Gtknotify::persistent_function_template-&InstanceTemplate()-&SetAccessor(String::New(&icon&), GetIcon, SetIcon);
&&& 声明原型方法:n.send()
NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, &send&, Send);
现在我们的Init()方法看起来应该是这样的:
// Our constructor
static v8::Persistent&FunctionTemplate& persistent_function_
static void Init(Handle&Object& target) {
&v8::HandleS // used by v8 for garbage collection
&// Our constructor
&v8::Local&FunctionTemplate& local_function_template = v8::FunctionTemplate::New(New);
&Gtknotify::persistent_function_template = v8::Persistent&FunctionTemplate&::New(local_function_template);
&Gtknotify::persistent_function_template-&InstanceTemplate()-&SetInternalFieldCount(1); // 1 since this is a constructor function
&Gtknotify::persistent_function_template-&SetClassName(v8::String::NewSymbol(&Notification&));
&// Our getters and setters
&Gtknotify::persistent_function_template-&InstanceTemplate()-&SetAccessor(String::New(&title&), GetTitle, SetTitle);
&Gtknotify::persistent_function_template-&InstanceTemplate()-&SetAccessor(String::New(&icon&), GetIcon, SetIcon);
&// Our methods
&NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, &send&, Send);
&// Binding our constructor function to the target variable
&target-&Set(String::NewSymbol(&notification&), Gtknotify::persistent_function_template-&GetFunction());
剩下要做的就是编写我们在Init方法中用的C++方法:New,GetTitle,SetTitle,GetIcon,SetIcon,Send
构造器方法: New()
New() 方法创建了我们自定义类的新实例(一个 Gtknotify 对象),并设置一些初始值,然后返回该对象的 JavaScript 处理。这是 JavaScript 使用 new 操作符调用构造函数的期望行为。
static Handle&Value& New(const Arguments& args) {
&Gtknotify* gtknotify_instance = new Gtknotify();
&gtknotify_instance-&title = &Node.js&;
&gtknotify_instance-&icon = &terminal&;
&gtknotify_instance-&Wrap(args.This());
&return args.This();
getters 和 setters: GetTitle(), SetTitle(), GetIcon(), SetIcon()
下面主要是一些样板代码,可以归结为 C++ 和 JavaScript (v8) 之间的值转换。
static v8::Handle&Value& GetTitle(v8::Local&v8::String& property, const v8::AccessorInfo& info) {
&Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap&Gtknotify&(info.Holder());
&return v8::String::New(gtknotify_instance-&title.c_str());
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 = *v8
static v8::Handle&Value& GetIcon(v8::Local&v8::String& property, const v8::AccessorInfo& info) {
&Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap&Gtknotify&(info.Holder());
&return v8::String::New(gtknotify_instance-&icon.c_str());
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 = *v8
原型方法: Send()
首先我们抽取 C++ 对象的 this 引用,然后使用对象的属性来构建通知并显示。
static v8::Handle&Value& Send(const Arguments& args) {
&v8::HandleS
&Gtknotify* gtknotify_instance = node::ObjectWrap::Unwrap&Gtknotify&(args.This());
&v8::String::Utf8Value v8str(args[0]);
&Notify::init(&Basic&);
&Notify::Notification n(gtknotify_instance-&title.c_str(), *v8str, gtknotify_instance-&icon.c_str());
&n.show();
&return v8::Boolean::New(true);
node-waf 是一个构建工具,用来编译 Node 的扩展,这是 waf 的基本封装。构建过程可通过名为 wscript 的文件进行配置。
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']
现在我们已经准备好要开始构建了,在顶级目录下运行如下命令:
node-waf configure && node-waf build
如果一切正常,我们将得到编译过的扩展,位于:./build/default/gtknotify.node ,来试试:
& var notif = require('./build/default/gtknotify.node');
& n = new notif.notification();
{ icon: 'terminal', title: 'Node.js' }
& n.send(&Hello World!&);
上述的代码将在你的屏幕右上方显示一个通知信息。
这是非常酷的, 但是怎样与Node社区分享你的努力的成果呢? 这才是npm主要的用途: 使它更加容易扩展和分发.
打npm的扩展包是非常简单的. 你所要做的就是在你的顶级目录中创建一个包含你的扩展信息的文件package.json :
&&name& : &notify&,
&&version& : &v0.1.0&
&, &scripts& : {
&&&&preinstall& : &node-waf configure && node-waf build&
&&&, &preuninstall& : &rm -rf build/*&
&, &main& : &build/default/gtknotify.node&
&, &description& : &Description of the extension....&
&, &homepage& : &&
&, &author& : {
&&&&name& : &Olivier Lalonde&
&&&, &email& : &&
&&&, &url& : &&
&, &repository& : {
&&&&type& : &git&
&&&, &url& : &&
关于package.json 格式的更多细节, 可以通过 npm help json 获取文档. 注意 大多数字段都是可选的.
你现在可以在你的顶级目录中通过运行npm install 来安装你的新的npm包了. 如果一切顺利的话, 应该可以简单的加载你的扩展 var notify = require('你的包名');. 另外一个比较有用的命令式 npm link 通过这个命令你可以创建一个到你开发目录的链接,当你的代码发生变化时不必每次都去安装/卸载.
假设你写了一个很酷的扩展, 你可能想要在中央npm库发布到网上. 首先你要先创建一个账户:
$ npm adduser
下一步, 回到你的根目录编码并且运行:
$ npm publish
就是这样, 你的包现在已经可以被任何人通过npm install 你的包名命令来安装了.
相关报道:
本站地址:
责任编辑:
已有位网友参与评论
本类周最热
分类导航: |
软件名称软件类别好评率
本站所有资源均来自互联网,本站只做收集,如本站侵犯到您的版权请发送邮件给我们,我们收到邮件后会在第一时间删除!商务QQ:1674653
Copyright (C)Node.js的模块写法入门_悬赏任务_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Node.js的模块写法入门
我需要一份与标题相关的文档
收到0篇文档
相似悬赏任务随笔 - 73&
评论 - 121&
&&&&&&&&&&&
本文主要备忘为Node.js编写组件的三种实现:纯js实现、v8 API实现(同步&异步)、借助swig框架实现。
关键字:Node.js、C++、v8、swig、异步、回调。
首先介绍使用v8 API跟使用swig框架的不同:
(1)v8 API方式为官方提供的原生方法,功能强大而完善,缺点是需要熟悉v8 API,编写起来比较麻烦,是js强相关的,不容易支持其它脚本语言。
(2)swig为第三方支持,一个强大的组件开发工具,支持为python、lua、js等多种常见脚本语言生成C++组件包装代码,swig使用者只需要编写C++代码和swig配置文件即可开发各种脚本语言的C++组件,不需要了解各种脚本语言的组件开发框架,缺点是不支持javascript的回调,文档和demo代码不完善,使用者不多。
二、纯JS实现Node.js组件
(1)到helloworld目录下执行npm init 初始化package.json,各种选项先不管,默认即可,更多package.json信息参见:/files/package.json。
(2)组件的实现index.js,例如:
module.exports.Hello = function(name) {
console.log('Hello ' + name);
(3)在外层目录执行:npm install ./helloworld,helloworld于是安装到了node_modules目录中。
(4)编写组件使用代码:
var m = require('helloworld');
m.Hello('zhangsan');
//输出: Hello zhangsan
三、 使用v8 API实现JS组件&&同步模式
&(1)编写binding.gyp,&eg:
"targets": [
"target_name": "hello",
"sources": [ "hello.cpp" ]
关于binding.gyp的更多信息参见:/nodejs/node-gyp
(2)编写组件的实现hello.cpp,eg:
#include &node.h&
namespace cpphello {
using v8::FunctionCallbackI
using v8::I
using v8::L
using v8::O
using v8::S
using v8::V
void Foo(const FunctionCallbackInfo&Value&& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello World"));
void Init(Local&Object& exports) {
NODE_SET_METHOD(exports, "foo", Foo);
NODE_MODULE(cpphello, Init)
(3)编译组件
node-gyp configure
node-gyp build
./build/Release/目录下会生成hello.node模块。
&(4)编写测试js代码
const m = require('./build/Release/hello')
console.log(m.foo());
//输出 Hello World
&(5)增加package.json 用于安装&eg:
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "node test.js"
"author": "",
"license": "ISC"
(5)安装组件到node_modules
进入到组件目录的上级目录,执行:npm install ./helloc&//注:helloc为组件目录
会在当前目录下的node_modules目录下安装hello模块,测试代码这样子写:
var m = require('hello');
console.log(m.foo());
四、 使用v8 API实现JS组件&&异步模式
上面三的demo描述的是同步组件,foo()是一个同步函数,也就是foo()函数的调用者需要等待foo()函数执行完才能往下走,当foo()函数是一个有IO耗时操作的函数时,异步的foo()函数可以减少阻塞等待,提高整体性能。
异步组件的实现只需要关注libuv的uv_queue_work API,组件实现时,除了主体代码hello.cpp和组件使用者代码,其它部分都与上面三的demo一致。
hello.cpp:
* Node.js cpp Addons demo: async call and call back.
* gcc 4.8.2
* author:cswuyg
#include &iostream&
#include &node.h&
#include &uv.h&
#include &sstream&
#include &unistd.h&
#include &pthread.h&
namespace cpphello {
using v8::FunctionCallbackI
using v8::F
using v8::I
using v8::L
using v8::O
using v8::V
using v8::E
using v8::P
using v8::HandleS
using v8::I
using v8::S
// async task
struct MyTask{
int output{0};
unsigned long long work_tid{0};
unsigned long long main_tid{0};
Persistent&Function&
// async function
void query_async(uv_work_t* work) {
MyTask* task = (MyTask*)work-&
task-&output = task-&a + task-&b;
task-&work_tid = pthread_self();
usleep(1000 * 1000 * 1); // 1 second
// async complete callback
void query_finish(uv_work_t* work, int status) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope handle_scope(isolate);
MyTask* task = (MyTask*)work-&
const unsigned int argc = 3;
stream && task-&main_
std::string main_tid_s{stream.str()};
stream.str("");
stream && task-&work_
std::string work_tid_s{stream.str()};
Local&Value& argv[argc] = {
Integer::New(isolate, task-&output),
String::NewFromUtf8(isolate, main_tid_s.c_str()),
String::NewFromUtf8(isolate, work_tid_s.c_str())
Local&Function&::New(isolate, task-&callback)-&Call(isolate-&GetCurrentContext()-&Global(), argc, argv);
task-&callback.Reset();
// async main
void async_foo(const FunctionCallbackInfo&Value&& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
if (args.Length() != 3) {
isolate-&ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "arguments num : 3")));
if (!args[0]-&IsNumber() || !args[1]-&IsNumber() || !args[2]-&IsFunction()) {
isolate-&ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "arguments error")));
MyTask* my_task = new MyT
my_task-&a = args[0]-&ToInteger()-&Value();
my_task-&b = args[1]-&ToInteger()-&Value();
my_task-&callback.Reset(isolate, Local&Function&::Cast(args[2]));
my_task-&work.data = my_
my_task-&main_tid = pthread_self();
uv_loop_t *loop = uv_default_loop();
uv_queue_work(loop, &my_task-&work, query_async, query_finish);
void Init(Local&Object& exports) {
NODE_SET_METHOD(exports, "foo", async_foo);
NODE_MODULE(cpphello, Init)
异步的思路很简单,实现一个工作函数、一个完成函数、一个承载数据跨线程传输的结构体,调用uv_queue_work即可。难点是对v8 数据结构、API的熟悉。
// test helloUV module
'use strict';
const m = require('helloUV')
m.foo(1, 2, (a, b, c)=&{
console.log('finish job:' + a);
console.log('main thread:' + b);
console.log('work thread:' + c);
finish job:3
main thread:640
work thread:848
五、swig-javascript 实现Node.js组件
利用swig框架编写Node.js组件
(1)编写好组件的实现:*.h和*.cpp&
namespace a {
int add(int a, int y);
int add(int x, int y);
(2)编写*.i,用于生成swig的包装cpp文件
/*&File&:&IExport.i&*/
%module&my_mod&
%include&"typemaps.i"
%include&"std_string.i"
%include&"std_vector.i"
#include&"export.h"
%apply&int&*OUTPUT&{&int&*result,&int*&xx};
%apply&std::string&*OUTPUT&{&std::string*&result,&std::string*&yy&};
%apply&std::string&&OUTPUT&{&std::string&&result&};&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
%include&"export.h"
namespace&std&{
&&&%template(vectori)&vector&int&;
& &%template(vectorstr)&vector&std::string&;
上面的%apply表示代码中的 int* result、int* xx、std::string* result、std::string* yy、std::string& result是输出描述,这是typemap,是一种替换。
C++导出函数返回值一般定义为void,函数参数中的指针参数,如果是返回值的(通过*.i文件中的OUTPUT指定),swig都会把他们处理为JS函数的返回值,如果有多个指针,则JS函数的返回值是list。
%template(vectori) vector&int& 则表示为JS定义了一个类型vectori,这一般是C++函数用到vector&int& 作为参数或者返回值,在编写js代码时,需要用到它。
swig支持的更多的stl类型参见:/swig/swig/tree/master/Lib/javascript/v8
(3)编写binding.gyp,用于使用node-gyp编译
(4)生成warpper cpp文件&生成时注意v8版本信息,eg:swig&-javascript&-node&-c++&-DV8_VERSION=0x040599&example.i
(5)编译&测试
难点在于stl类型、自定义类型的使用,这方面官方文档太少。
swig - javascript对std::vector、std::string、的封装使用参见:,主要关注*.i文件的实现。
在使用v8 API实现Node.js组件时,可以发现跟实现Lua组件的相似之处,Lua有状态机,Node有Isolate。
Node实现对象导出时,需要实现一个构造函数,并为它增加&成员函数&,最后把构造函数导出为类名。Lua实现对象导出时,也需要实现一个创建对象的工厂函数,也需要把&成员函数&们加到table中。最后把工厂函数导出。
Node的js脚本有new关键字,Lua没有,所以Lua对外只提供对象工厂用于创建对象,而Node可以提供对象工厂或者类封装。
本文所在:&
参考资料:
1、v8 API参考文档:/node-5.0/index.html
2、swig-javascript文档:http://www.swig.org/Doc3.0/Javascript.html
3、C++开发Node.js组件:https://nodejs.org/dist/latest-v4.x/docs/api/addons.html#addons_addons
4、swig-javascript demo:/swig/swig/tree/master/Examples/javascript/simple
5、C++开发Node.js组件 demo:/nodejs/node-addon-examples
阅读(...) 评论()}

我要回帖

更多关于 编写js的工具 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信