通常,我们开发原生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,你需要了解一些基本知识:

  • Libuv

  • V8

  • Node.js internals

推荐阅读这些资料。

创建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 typeif (!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" ]}]
}

编译环境配置:

  • windows: 以管理员的身份运行npm install --global --production windows-build-tools,这个会安装所有编译依赖的工具。

  • linux: 安装python v2.7、make和GCC

  • osx: 安装xcode

虽然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个台阶的所有方法

本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1932206 ,如需转载请自行联系原作者

编写原生的Node.js模块相关推荐

  1. node mocha_如何使用Mocha和Assert测试Node.js模块

    node mocha The author selected the Open Internet/Free Speech Fund to receive a donation as part of t ...

  2. Node.js「一」—— Node.js 简介 / Node.js 模块 / 包 与 NPM

    本文为 Node.js 系列笔记第一篇.文章参考:nodejs 教程 -- 大地:<深入浅出 Node.js>:阮一峰 nodejs 博客 文章目录 一.Node 简介 1. 简单介绍 2 ...

  3. 不再为 Node.js 模块自动引入 Polyfills

    在早期,webpack 的目的是为了让大多数的 Node.js 模块运行在浏览器中,但如今模块的格局已经发生了变化,现在许多模块主要是为前端而编写.webpack <= 4 的版本中提供了许多 ...

  4. node 大写_大写Node.js模块

    node 大写 Today, let's see a third party module that helps us in working with upper-case letters witho ...

  5. Node.js 模块之Nimble流程控制

    NodeJS异步的特性有时候会导致程序非常的难看,回调一层套着一层,这个时候就要用流程控制模块来控制究竟是同步还是异步了. Nimble是一个轻量.可移植的函数式流程控制模块.经过最小化和压缩后只有8 ...

  6. 如何使用async / await和Firebase数据库编写漂亮的Node.js API

    by Paul Breslin 保罗·布雷斯林(Pau​​l Breslin) 如何使用async / await和Firebase数据库编写漂亮的Node.js API (How to write ...

  7. (8)Node.js 模块介绍

    一.Node.js模块介绍 模块(包)是 Node.js 中具有特定功能的对象. 二.web浏览器端和Node端的对比图 我们通过如上图可以看到,再web浏览器端的基本语法,再Node端也能使用,但是 ...

  8. node.js 模块_如何创建Node JS可重用模块

    node.js 模块 In my previous post, we have discussed about "How to export and import a Node JS Mod ...

  9. Node.js---02、node.js 模块加载机制

    2019独角兽企业重金招聘Python工程师标准>>> node.js模块分为自定义模块.扩展模块和核心模块.所有模块的加载都是通过关键字require()实现. 1.自定义模块的加 ...

最新文章

  1. C++复制控制:拷贝构造函数
  2. 厦门大学计算机专业录取分数线2019,厦门大学2019年本科生录取分数线
  3. 独家 | 一文读懂Apache Flink技术
  4. php7.1 改动,PHP7错误处理机制修改
  5. C#可用的日出日落时间类
  6. python 监视图_python获取zabbix监控图
  7. 前端学习(1808):前端调试之微博头部开发
  8. spring学习(39):注入map类型
  9. [转载]了解Linux的进程与线程
  10. 深入理解JVM(4)——如何优化Java GC
  11. 工控HMI界面设计基本原则
  12. RocKey4加密狗复制软件及教程
  13. 使用python+selenium谷歌浏览器驱动查排名
  14. 78个学术网站!史上最全常用文献数据库汇总!
  15. 虚幻4_添加武器插槽到骨骼
  16. 关于VMware VAAI说明
  17. IT咨询,从问题到主义
  18. 1953年克里克和沃森发现DNA双螺旋结构
  19. NanShan HTML 5与Flex是两种截然不同的技术解决方案
  20. 用Python写一个天天酷跑

热门文章

  1. 《Hadoop MapReduce实战手册》一1.4 给WordCount MapReduce程序增加combiner步骤
  2. composer的简单使用
  3. ios网址解析中,中文部分如何处理
  4. hasOwnProperty()
  5. Cognos 10.1 饼图百分比小数点显示的问题
  6. memmove() -- 拷贝内存内容
  7. 按 结构记录的 相关字段 快速排序
  8. sqlserver死锁解决的存储过程
  9. Hive的列分隔符和行分隔符
  10. Linux目录结构介绍