WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如C/C++/Rust等语言提供一个编译目标,以便它们可以在Web上运行。它也被设计为可以与JavaScript共存,允许两者一起工作。1
根据调研,WebAssembly经常与用来和现存的JavaScript一起工作。接下来讲一下我是如何Node.js与WebAssembly一起工作的
新建项目
首先使用cargo新建一个库项目,名称叫做wasm-module
cargo new --lib wasm-module
在其自动生成的Cargo.toml文件中添加如下语句
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen="0.2"
前两行是关闭wasm-opt优化,此优化需要安装额外工具且较为复杂,此处先禁用
中间两行声明了该项目是一个兼容C语言接口的动态链接库dylib
后两行为该项目添加了新的依赖wasm-bindgen,用于处理Node.js与Rust中的交互接口
项目的编写
首先lib.rs可以参考如下的示例文件来编写
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
macro_rules! console_warn {
($($t:tt)*) => (warn(&format_args!($($t)*).to_string()))
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_namespace = console)]
fn warn(s: &str);
}
#[wasm_bindgen]
pub fn hello(name: &str) {
console_log!("Hello, {}!", name);
}
#[wasm_bindgen]
pub fn strlen(k: &str) -> i32 {
k.len() as i32
}
在此处,需要注意的是,如果需要与JS交互(调用JS或者被JS调用)的函数都应该在函数之前写#[wasm_bindgen]宏,用于在项目构建时自动处理该函数。
而需要调用JS的函数需要对函原型数进行声明,写在extern "C"块中。较为复杂的函数,如console.log,可以参考如上写法,在#[wasm_bindgen]中添加js_namespace项从而指定JS中的函数
以上的自定义两个宏console_log!和console_warn!是对log函数与warn函数的一个包装,使其与Rust中原生输出方式println!和print!更加相似,可以使用格式化字符串的方式进行输出。
注意:在JS中的类型与Rust中的类型的对应关系请参考wasm-bindgen文档 支持的类型2
项目的构建和发布
在MDN1的参考文档中,它是使用wasm-pack来进行wasm模块的打包,并且最终是使用一个简易的网页来加载。但在本项目中,大部分操作都会位于Node.js中,所以需要额外的打包工具并且使用如下的打包方式
- 首先安装
wasm-packcargo install wasm-pack - 然后使用
wasm-pack进行打包wasm-pack build --target nodejs - 生成的ES Module在pkg文件夹中,可以自行将其移动到指定的位置进行调用
在最后我们可以新建一个typescript文件(或者javascript文件)来验证一下接口是否正常工作。
import * as wasm_module from '(替换为pkg文件夹的路径)'
wasm_module.hello("WASM")
得到程序的输出
Hello, WASM!
则说明接口模块正常工作,至此就完成了一个简易的wasm模块的编写