Skip to content
0

Rust bindings for NodeJs

在前端的工程领域,Rust 语言编写的工具大放异彩,为前端开发体验带来了质的飞跃

下面,我将解释为什么我们能在 js 中调用 rust 语言编写的程序

.node 模块

在 NodeJs 中,除了可以原生加载 js 模块,还可以加载 node 原生模块,这种模块以 .node 扩展名结尾

c++ to node 一段中,阐述了如何通过 node-gyp,将 C++ 代码编译为 node 模块,以便于在 NodeJs 中使用

简而言之,在安装了 NodeJs 之后,我们可以在 C++ 代码中,直接引入 NodeJs 提供的头文件 #include <node.h>,以此来编写相关的代码

#include <node.h>
namespace demo {
  //...
}

经过构建后,我们就可以得到 node 模块,在 js 中使用:

import addon from './build/Release/binding.node'

console.log(
    addon.hello() //=> world
)

C/C++ 代码被允许访问、创建和操作 JavaScript 对象,就像它们是由 JavaScript 代码创建的一样。C++ 通常比 Js 执行速度更快,所以性能是 node 模块的天然优势之一

NAPI

Node-API 是 Node 8.0.0 中引入的工具包,以便开发人员可以编写一次性的 C++ 代码,在不同版本的 NodeJs 和平台上运行

#include <node.h>
#include <napi.h>

因此,当我们想用 C++ 来构建一个 node 模块,首选 NAPI

napi-rs

所谓 napi-rs,顾名思义,就是用 rust 语言编写的 node-api 的实现,参考 napi-rs/napi-rs。这个项目,使得我们可以快速使用 rust 语言分发 node 模块,提供给 js 使用

我们知道,NodeJs 帮我们抹去了平台之间差异,比如 arm64 和 x86。使得我们可以一套 JS 代码,同时跑在 MacOS、Windows 和 Linux 上

而 node 模块,作为一个二进制模块,不具有跨平台的特性。

假设我们在 MacOS 平台上构建原生模块,得到了 node 模块。当把源码拷贝到 Linux 上的时候,很可能程序就不能运行了,因为这个 node 模块是在另一个平台架构下构建出来了

因此,为了适配不同的系统架构,我们通常需要在不同的机器下构建,得到适配不同平台的 node 模块。当我们的 js 代码运行的时候,根据当前的平台去加载不同的 node 模块

.node 模块和 .js 模块直接的“桥梁” js 文件通常长这样:

let nativeBinding = null

switch (platform) {
  case 'android':
    switch (arch) {
      case 'arm64':
        localFileExisted = existsSync(join(__dirname, 'utils-rs.android-arm64.node'))
        try {
          if (localFileExisted) {
            nativeBinding = require('./utils-rs.android-arm64.node')
          } else {
            nativeBinding = require('@peterroe/utils-rs-android-arm64')
          }
        } catch (e) {
          loadError = e
        }
        break
      case 'arm':
        nativeBinding = require('./utils-rs-android-arm-eabi.node')
      default:
        throw new Error(`Unsupported architecture on Android ${arch}`)
    }
  case 'win32':
    switch (arch) {
      case 'x64':
      case 'ia32':
      case 'arm64':
      case 'darwin':
    switch (arch) {
      case 'x64':
      case 'arm64':
  case 'freebsd':
  case 'linux':
      case 'arm64':
      case 'arm':
      case 'riscv64':
      case 's390x':

const { sum } = nativeBinding

module.exports.sum = sum

因此,当我们想发布一个平台兼容性很好的 rust 编写的工具,需要发大量的 NPM 模块,每个 NPM 模块包含特定平台的编译出来的 .node 模块。例如 rspack:

Reference

https://nodejs.github.io/node-addon-examples/about/what/

Released under the MIT License.