准备开始

首先我们用下面的目录结构来创建一个节点通知(node-notify)文件夹.
 

.

|-- build/                   # This is where our extension is built.

|-- demo/

|   `-- demo.js              # This is a demo Node.js script to test our extension.

|-- src/

|   `-- 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前期看起来应该像:
 

// This loads our extension on the notify variable.
// It will only load a constructor function, notify.notification().
var notify = require("../build/default/gtknotify.node"); // path to our extension
 
var notification = new notify.notification();
notification.title = "Notification title";
notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16
notification.send("Notification message");

编写我们的Node.js扩展
Init方法

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

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

#include <v8.h> // v8 is the Javascript engine used by QNode
#include <node.h>
// We will need the following libraries for our GTK  notification
#include <string>
#include <gtkmm.h>
#include <libnotifymm.h>
 
using namespace v8;
 
class Gtknotify : node::ObjectWrap {
 private:
 public:
  Gtknotify() {}
  ~Gtknotify() {}
  static void Init(Handle<Object> target) {
   // This is what Node will call when we load the extension through require(), see boilerplate code below.
  }
};
 
/*
 * WARNING: Boilerplate code ahead.
 *
 * See https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/ & http://www.freebsd.org/cgi/man.cgi?query=dlsym
 * 
 * Thats it for actual interfacing with v8, finally we need to let Node.js know how to dynamically load our code.
 * Because a Node.js extension can be loaded at runtime from a shared object, we need a symbol that the dlsym function can find,
 * so we do the following: 
 */
 
v8::Persistent<FunctionTemplate> Gtknotify::persistent_function_template;
extern "C" { // Cause of name mangling in C  , we use extern C here
 static void init(Handle<Object> target) {
  Gtknotify::Init(target);
 }
 // @see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101
 NODE_MODULE(gtknotify, init);
}

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

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

// Wrap our C   New() method so that it's accessible from Javascript
  // This will be called by the new operator in Javascript, for example: new notification();
  v8::Local<FunctionTemplate> local_function_template = v8::FunctionTemplate::New(New);
   
  // Make it persistent and assign it to persistent_function_template which is a static attribute of our class.
  Gtknotify::persistent_function_template = v8::Persistent<FunctionTemplate>::New(local_function_template);
   
  // Each JavaScript object keeps a reference to the C   object for which it is a wrapper with an internal field.
  Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object
  // Set a "class" name for objects created with our constructor
  Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
   
  // Set the "notification" property of our target variable and assign it to our constructor function
  target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());

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

  // 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()

    声明原型方法:n.send()
   
  // 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()方法看起来应该是这样的:
 
// Our constructor
static v8::Persistent<FunctionTemplate> persistent_function_template;
 
static void Init(Handle<Object> target) {
 v8::HandleScope scope; // 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 操作符调用构造函数的期望行为。

 
std::string title;
std::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) 之间的值转换。
 

// 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 引用,然后使用对象的属性来构建通知并显示。
 

// 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 的文件进行配置。
 

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 ,来试试:
 

$ 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 :
 

{
 // 扩展的名称 (不要在名称中包含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" : "olalonde@gmail.com"
   , "url" : "http://www.syskall.com/"
  }
 , "repository" : {
   "type" : "git"
   , "url" : "https://github.com/olalonde/node-notify.git"
  }
}

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


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

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

 

$ npm adduser

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

 
$ npm publish

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

 

使用Node.js为其他程序编写扩展的基本方法的更多相关文章

  1. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  3. Node.js调试技术总结分享

    Node.js是一个可以快速构建网络服务及应用的平台。该平台的构建是基于Chrome's JavaScript runtime,也就是说,实际上它是对Google V8引擎(应用于Google Chrome浏览器)进行了封装。 今天介绍Node.js调式目前有几种技术,需要的朋友可以参考下。

  4. node.js实现http服务器与浏览器之间的内容缓存操作示例

    这篇文章主要介绍了node.js实现http服务器与浏览器之间的内容缓存操作,结合实例形式分析了node.js http服务器与浏览器之间的内容缓存原理与具体实现技巧,需要的朋友可以参考下

  5. 教你如何使用node.js制作代理服务器

    本文介绍了如何使用node.js制作代理服务器,图文并茂,十分的详细,代码很简洁易懂,这里推荐给大家。

  6. node.js中的fs.openSync方法使用说明

    这篇文章主要介绍了node.js中的fs.openSync方法使用说明,本文介绍了fs.openSync方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下

  7. Node.js+ELK日志规范的实现

    这篇文章主要介绍了Node.js+ELK日志规范的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  8. node.js爬虫框架node-crawler初体验

    这篇文章主要介绍了node.js爬虫框架node-crawler的相关资料,帮助大家利用node.js进行爬虫,感兴趣的朋友可以了解下

  9. node.js中的fs.existsSync方法使用说明

    这篇文章主要介绍了node.js中的fs.existsSync方法使用说明,本文介绍了fs.existsSync方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下

  10. 说说如何利用 Node.js 代理解决跨域问题

    这篇文章主要介绍了Node.js代理解决跨域问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部