所有的在Electron中都可用,并且所有的node的第三方NPM组件也可以放心使用。

    Node自带的API文档可查看,内建的模块包括crypto用于加解密算法;fs模块用于操作系统的文件操作;http和https用于创建http/https服务端或者客户端请求;net模块是底层的tcp通信操作。

    目前有两种使用方式:解构使用和传统使用

    解构任务

    由于目前TitanOne所使用Electron的版本支持ES6,所以可以使用destructuring assignment来让使用内置模块更简单:

    传统样式

    在版本v0.35.0之前,所有的内置模块都需要按造形式来使用,虽然它有很多,我们仍然在老的应用中友好的支持它。

    1. const app = require('electron').app;
    2. const BrowserWindow = require('electron').BrowserWindow;

    Node支持C/C++开发的Addon组件,用C++编写的模块和用JavaScript编写的模块在使用方式上并无区别,都是通过require(…)来进行调用,区别在于C++模块是系统编译好的二进制模块(在*nix下是.so,在win下是dll),并且扩展名为.node。在require.node模块的时候,系统通过dlopen函数来加载模块,不需要像JavaScript模块那样再进行编译,而是直接加载运行,这加快了执行的速度。更多内容可以参考官方文档:Node‘s Addon

    Electron 同样也支持Node的Addon模块,但由于和官方的 Node 相比使用了不同的 V8 引擎,如果要使用C/C++的原生组件就需要重新编译Addon模块。

    Node的组成部分

    写一个Node的Addon是相当的复杂,需要有C/C++知识,同时要懂JavaScript的引擎的接口,同时需要知道Node的异步事件引擎LibUV

    首先我们必须得了解Node的组成,Node其实本质就是具有IO操作功能,并通过JS来暴露API的应用程序,在Node中最重要的就是V8和LibUV,下面详细说明了Node的各个组成部分与功能:

    • V8:Google 推出的 Javascript VM,也是 Node.js 为什么使用的是 Javascript 的关键,它为 Javascript 提供了在非浏览器端运行的环境,它的高效是 Node.js 之所以高效的原因之一。

    • Libuv:它为 Node.js 提供了跨平台,线程池,事件池,异步 I/O 等能力,是 Node.js 如此强大的关键。

    • C-ares:提供了异步处理 DNS 相关的能力。

    • http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、数据压缩等其他的能力。

    开发环境

    开发环境的搭建主要要安装编译工具和C++的IDE,更多内容参考:

    • 要写C++模块,必须先搭建一个环境,node-gyp命令是必须,所以我们得先安装node-gyp
    1. npm install -g node-gyp
    • 安装

    开发Addon

    Create an empty C/C++ fil创建一个C或者C++的文件,如:binding.cc

    然后创建一个binding.gyp文件,在binding.gyp文件中对应的生成的目标node文件名和源码的文件名。

    1. {
    2. "targets": [
    3. {
    4. "target_name": "addon",
    5. "sources": [ "binding.cc" ]
    6. }
    7. ]
    8. }

    binding.ccbinding.gyp放置在同一个目录中,利用IDE对binding.cc进行开发。

    我们测试编写了一个最简单的函数hello通过V8暴露出来,函数输出“world"字符串。

    1. #include <node.h>
    2. #include <v8.h>
    3. using namespace v8;
    4. void Method(const v8::FunctionCallbackInfo<v8::Value>& args) {
    5. v8::HandleScope scope(isolate);
    6. args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world"));
    7. }
    8. void init(v8::Local<v8::Object> target) {
    9. NODE_SET_METHOD(target, "hello", Method);
    10. }
    11. NODE_MODULE(binding, init);

    编译Addon

    首先调用命令来生成生成了Visual Studio的工程文件,具体的命令参数参考Node-gyp Command

    生成的工程文件在build文件夹中,文件列表如下:

     Node组件  - 图1

    最后调用命令来生成.node的二进制文件,完成编译工作。

      build文件夹下面生成Release文件夹,文件夹中有我们需要的目标文件addon.node,如下所示:

      使用C/C++ Addon

      我们创建一个测试的js文件来测试上面编译生成的addon.node组件,测试test.js如下:

      1. 'use strict';
      2. var addon = require('./build/Release/addon');
      3. console.log('addon.hello() =', binding.hello());
      1. node test.js

      测试结果:

      1. addon.hello() = world

      如何编译为Electron组件?

      在上面介绍了如何开发Node的C/C++的Addon组件,但是由于Electron使用的Node版本可能跟你本机编译的Node版本不一致,所以需要再次编译才能在Electron上使用,可以在查看 Electron 内置的 Node 版本,或者在Electron的chrome调试器使用 process来查看。

       Node组件  - 图2

      我们目前使用的Electron版本为1.2.5,所附带的Node版本为6.1.0,架构为32位的应用程序,得到这些信息以后就可以用node -gyp再次编译上面的源码,重新生成的addon.node才可以被Electron使用。

      利用上面测test.js在Electron上面测试,在控制台正确输出了结果,证明C/C++能够在Electron中正常使用:

      node-ffi 是能够被Node.js加载的组件,并且能够用纯JavaScript来对dll进行调用,它是的原生的动态库与Node.js的粘合剂。

      如果原来的模块是一个DLL,并且已经有了接口,我们就可以利用FFI组件来调用该DLL,只需要告诉FFI如何来调用该DLL的导出接口,而不需要对DLL重新封装成Node组件。

      安装Node-FFI

      首先在目录下安装ffi组件

      在ffi的子目录下存在多个原生C/C++的addon,需要用下面命令来重新编译生成。

      1. node-gyp rebuild --target=1.2.5 --arch=ia32 --dist-url=https://atom.io/download/atom-shell

      测试使用

      测试的dll,导出函数为C类型的标准导出,计算输入参数的阶乘,代码如下:

      1. extern "C" __declspec(dllexport) uint64_t factorial(int max) {
      2. int i = max;
      3. uint64_t result = 1;
      4. while (i >= 2) {
      5. result *= i--;
      6. }
      7. return result;
      8. }

      测试的js代码如下,在里面要注明导出函数和返回值类型和输入参数类型:

      1. var ffi = require('ffi');
      2. var libfactorial =ffi.Library(__dirname+'/factorial.dll',{
      3. 'factorial':['uint64',['int']]//dll的导出函数名称:[函数返回类型,[输入参数类型]]
      4. });

      测试结果界面,通过ffi调用dll里面的阶乘算法,计算出来结果。