图片来源: https:// rustwasm.github.io/
本文作者:刘家隆
写在前边
本文希望通过 Rust 敲一敲 WebAssembly 的大门。作为一篇入门文章,期望能够帮你了解 WebAssembly 以及构建一个简单的 WebAssembly 应用。在不考虑IE的情况,目前大部分主流的浏览器已经支持 WebAssembly,尤其在移动端,主流的UC、X5内核、Safari等都已支持。读完本文,希望能够帮助你将 WebAssembly 应用在生产环境中。
WebAssembly(wasm) 简介
如果你真的了解了 WebAssembly, 可以跳过这一节。
可以先看两个 wasm 比较经典的 demo: http:// webassembly.org.cn/demo /Tanks/ http:// wasm.continuation-labs.com /d3demo/
快速总结一下: WebAssembly(wasm) 是一个可移植、体积小、加载快并且兼容 Web 的全新格式,由 w3c 制定出的新的规范。目的是在一些场景下能够代替 JS 取得更接近原生的运算体验,比如游戏、图片/视频编辑、AR/VR。说人话,就是可以体积更小、运行更快。
wasm 有两种表示格式,文本格式和二进制格式。二进制格式可以在浏览器的 js 虚拟机中沙箱化运行,也可以运行在其他非浏览器环境中,比如常见的 node 环境中等;运行在 Web 上是 wasm 一开始设计的初衷,所以实现在浏览器上的运行方法非常简单。
通过一个简单的例子实现快速编译 wasm 文本,运行一个 wasm 二进制文件:
wasm 文本格式代码:
(module
(import "js" "import1" (func $i1)) // 从 js 环境中导入方法1
(import "js" "import2" (func $i2)) // 从 js 环境中导入方法2
(func $main (call $i1)) // 调用方法1
(start $main)
(func (export "f") (call $i2)) // 将自己内部的方法 f 导出,提供给 js,当 js 调用,则会执行方法2
)
上述内容看个大概即可,参阅代码中注释大致了解主要功能语法即可。主要功能就是从 js 环境中导入两个方法 import1
和 import2
; 同时自身定义一个方法 f
并导出提供给外部调用,方法体中执行了 import2
。
文本格式本身无法在浏览器中被执行,必须编译为二进制格式。可以通过 wabt 将文本格式编译为二进制,注意文本格式本身不支持注释的写法,编译的时候需要将其去除。这里使用 wat2wasm 在线工具快速编译,将编译结果下载就是运行需要的 wasm 二进制文件。
有了二进制文件,剩下的就是在浏览器中进行调用执行。
// 定义 importObj 对象赋给 wasm 调用
var importObj = {js: {
import1: () => console.log("hello,"), // 对应 wasm 的方法1
import2: () => console.log("world!") // 对应 wams 的方法2
}};
// demo.wasm 文件就是刚刚下载的二进制文件
fetch('demo.wasm').then(response =>
response.arrayBuffer() // wasm 的内存 buffer
).then(buffer =>
/**
* 实例化,返回一个实例 WASM.module 和一个 WASM.instance,
* module 是一个无状态的 带有 Ast.module 占位的对象;
* 其中instance就是将 module 和 ES 相关标准融合,可以最终在 JS 环境中调用导出的方法
*/
WebAssembly.instantiate(buffer, importObj)
).then(({module, instance}) =>
instance.exports.f() // 执行 wasm 中的方法 f
);
大概简述一下功能执行流程:
- 在 js 中定义一个
importObj
对象,传递给 wasm 环境,提供方法import1
import2
被 wasm 引用; - 通过 fetch 获取二进制文件流并获取到内存 buffer;
- 通过浏览器全局对象 WebAssembly 从内存 buffer 中进行实例化,即
WebAssembly.instantiate(buffer, importObj)
,此时会执行 wasm 的main
方法,从而会调用import1
,控制台输出 hello; - 实例化之后返回 wasm 实例,