编写原生Node.js模块
导语:当Javascript的性能需要优化,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了。
应用场景
日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项目中进行使用。下面有个列表,你可能对它们的名字很熟悉:
- node-microtime: 扩展Javascript的时间精度
- node-inspector:进行调试
- v8-profiler:性能及内存使用分析
通常,我们开发原生Node.js模块包括但不仅限于以下原因:
- 对性能有比较苛刻要求的应用。尽管Node.js得益于libuv,在异步I/O操作很有优势,但遇到数字计算时并不是一个很好的选择。
- 使用更加低级的API,比如操作系统层面的。
- 在C/C++和Node.js之间创建一个Bridge,进行通信。
什么是原生模块?
Node.js Addons是动态链接的可共享对象,由C/C++编写而成。可以在Node.js中通过require()
方法进行调用,使用起来像调用Node.js普通模块一样。 —— 来自Node.js官方文档
这意味着如果处理得当的话,模块调用者使用由C/C++编写的原生模块的方式和由Node.js编写的模块一样。想要编写Node.js addons,你需要了解一些基本知识:
推荐阅读这些资料。
创建Node.js的原生扩展模块
下面我以一个常见的动态规划问题-青蛙跳台阶为例子来说明如何创建一个原生的Node.js模块。青蛙跳台阶描述为:一只青蛙一次可以跳上一级台阶,也可以跳上2级台阶,求该青蛙跳上n级台阶的共有多少种跳法?
首先创建一个frog_jump.cc原生文件,.cc的意思是c with class,扩展名也可以是.cpp。Google Style Guide建议使用.cc,那么此处还是以.cc做为扩展名吧。代码如下:
#include <node.h>
#include<vector>
/**
* Native method, calculate all ways frog jump to a target stair.
*/
int climbStairs(int n) {
std::vector<int> dp(n);
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i ++ ) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
/**
* Export native method jumpTo
*/
void JumpTo(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
// Check input type
if (!args[0] -> IsNumber()) {
isolate -> ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate, "Wrong arguments type!")));
}
int value = climbStairs(args[0] -> NumberValue());
v8::Local<v8::Number> num = v8::Number::New(isolate, value);
args.GetReturnValue().Set(num);
}
// init is entry point.
void init(v8::Local<v8::Object> exports) {
NODE_SET_METHOD(exports, "jumpTo", JumpTo);
}
NODE_MODULE(frog_jump, init)
对这段代码的解释:
- #include "node.h" 是c++里面引入头文件的方式,具体源码:node.h,C++链接时会加载这个头文件。头文件里面引入了v8命名空间,我们可以通过
v8::
标志来访问v8的接口。访问所有v8的类型,都需要使用v8::标志 - 通过args对象来访问Node.js传递过来的参数,通过args也可以获取调用相关信息。
- 通过v8::Isolate*可以获取函数作用域,可以像JS里面一样进行变量赋值,而不用担心垃圾回收问题,垃圾回收器会自动进行。
- args.GetReturnValue()可以对函数返回的结果进行设置。
- 任何原生Node.js模块都需要调用NODE_MODULE,NODE_MODULE是一个宏,它会进行模块注册操作。
- C++ 有丰富的内置类型来保存数字或者字符串,但是JS只能识别v8::里面定义的类型。因此,将c++的变量赋值给JS时,需要转换成可以被JS识别的类型,也即是v8::定义的类型。比如v8::String、v8::Object。
编译原生的Node.js模块
一旦源代码编写完成,需要将它编译成二进制的addon.node
文件,之后才能被Node.js require。为了完成编译操作,需要在项目的根目录创建binding.gyp文件,里面定义了Build的配置。binding.gyp的内容是一个JSON。
{
"targets": [
{
"target_name": "frog_jump",
"sources": [ "frog_jump.cc" ]
}
]
}
虽然npm内置了一个node-gyp版本,但是这个版本没有开放给开发者进行调用。npm install的时候会调用它来进行编译和安装工作。因此,开发者想要调用node-gyp必须自己安装一个全局的node-gyp版本。
$ npm install node-gyp -g
$ node-gyp configure
$ node-gyp build
运行node-gyp configure命令会生成一个跨平台的build文件,unix环境会生成Makefile,windows环境会在build目录里面生成vcxproj。
运行node-gyp build命令会生成可被Node.js调动的addon.node二进制文件。
Node.js中调用原生模块
const frogJump = require('./build/Release/frog_jump');
frogJump.jumpTo(20); //青蛙跳到第20个台阶的所有方法
项目源代码:frog-jump
后续
nan,即Native Abstractions for Node.js。它基于Node.js API接口,兼容所有Node版本,目前的最佳实践是基于nan来扩展原生模块,而不是直接使用Node.js API。
N-API,Node官方推出的用来编写原生Node扩展模块,是V8和nan的替代,目前处于实验阶段。
编写原生Node.js模块的更多相关文章
- 编写原生的Node.js模块
导语:当Javascript的性能遭遇瓶颈,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了. 应用场景 日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项 ...
- node.js系列(实例):原生node.js+formidable模块实现简单的文件上传
/** * 原生node.js结合formidable模块实现图片上传改名 * @Author:Ghost * @Date:2016/07/15 * @description: * 1.引入模块htt ...
- node.js系列(实例):原生node.js实现接收前台post请求提交数据
前台界面: 前台代码: <form class="form-horizontal" method="post" action="http:127 ...
- 10、Node.js模块系统
##################################################################################介绍Node.js模块系统为了让No ...
- koa 基础(十)原生node.js 在 koa 中获取表单提交的数据
1.app.js // 引入模块 const Koa = require('koa'); const router = require('koa-router')(); /*引入是实例化路由 推荐*/ ...
- Node.js模块
每一个Node.js都是一个Node.js模块,包括JavaScript文件(.js).JSON文本文件(.json)和二进制模块文件(.node). mymodul.js function Hell ...
- 如何发布一个自定义Node.js模块到NPM(详细步骤)
咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...
- Node.js模块封装及使用
Node.js中也有一些功能的封装,类似C#的类库,封装成模块这样方便使用,安装之后用require()就能引入调用. 一.Node.js模块封装 1.创建一个名为censorify的文件夹 2.在c ...
- 如何发布一个自定义Node.js模块到NPM(详细步骤,附Git使用方法)
咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...
随机推荐
- Android -- 从源码带你从EventBus2.0飚到EventBus3.0(一)
1,最近看了不少的面试题,不管是百度.网易.阿里的面试题,都会问到EventBus源码和RxJava源码,而自己只是在项目中使用过,却没有去用心的了解它底层是怎么实现的,所以今天就和大家一起来学习学习 ...
- org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection
转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6709758.html Android Studio导入项目报错: org.gradle.api.inter ...
- Reids详解-抄本
1.redis是什么 Redis 是一个基于内存的高性能key-value数据库.是一个开源的.使用C语言编写的.支持网络交互的.可基于内存也可持久化的Key-Value数据库. 2.redis的特点 ...
- yeoman 使用问题总结
1.今天尝试使用yeoman,执行grunt server 时报错: cannot find where you keep your bower packges 需要将bower npm instal ...
- 【从无到有】教你使用animation做简单的动画效果
今天写写怎么用animation属性做一些简单的动画效果 在CSS选择器中,使用animition动画属性,调用声明好的关键帧 首先声明一个动画(关键帧): @keyframes name{ from ...
- C#网络程序设计(2)Socket基础编程
本节介绍如何使用基础Socket实现TCP通信. (1)Socket详细介绍: Socket的英文原义是"孔"或"插座".通常称作"套 ...
- js事件相关面试题
说是面试题,其实也相当于是对js事件部分知识点的一个总结.简单内容一笔带过,了解详情我都给出了参考链接,都是之前写的一些相关文章.JavaScript本身没有事件模型,但是环境可以有. DOM:add ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(二)
<JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...
- Oracle解析复杂json的方法
问题背景: 当前在Oracle数据库(11G之前的版本)解析json没有可以直接使用的系统方法,网上流传的PLSQL脚本大多也只可以解析结构较单一的json串,对于结构复杂的json串还无法解析.如此 ...
- python基础——正则表达式
正则表达式 正则表达式为高级的文本模式匹配.抽取.与/或文本形式的搜索和替换功能提供了基础.简单的说,正则表达式是一些由字符和特殊符号组成的字符串,他们描述了模式的重复或者表述多个字符,于是正则表达式 ...