WebAssembly 基本介绍

423 阅读4分钟

WebAssembly 系列

WebAssembly 基本介绍

WebAssembly 对象

WebAssembly 文本格式

WebAssembly 是一种新的编码方式,它是继html、css、JavaScript之后又一种可以在浏览器中运行的文件格式。

设计初衷

WebAssembly 被设计出来的初衷主要有两点:

  • 更高的性能。鉴于 JavaScript 是一种解释性语言,在浏览器中的执行性能受到了限制,从而导致很多需要极高性能的web应用也遇到了发展瓶颈。因此,WebAssembly 作为一种低级的类汇编语言应运而生,其具有紧凑的二进制格式,可以接近原生的性能运行。

  • 更广的应用。目前,要编写web应用需要前端工程师编写相应的前端代码。如果能为后端语言提供一个编译目标,这个编译的产物可以运行在浏览器中,那么大量从事后端的开发者就可以完成前端开发任务。而 WebAssembly 就是这样的一个编译目标。目前,C / C ++ / Rust / Go / Java 等语言都可以编译成 WebAssembly。

image.png

WebAssembly 与 JavaScript

在 WebAssembly 出现之前,前端的逻辑都是由 JavaScript 实现的,那么 WebAssembly 的出现是否会取代 JavaScript 呢?

答案是 不会的

WebAssembly 的初衷不是用来取代 JavaScript。相反,它被设计为和 JavaScript 一起协同工作,从而使得网络开发者能够利用两种语言的优势:

  • JavaScript 是一门高级语言。对于写网络应用程序而言,它足够灵活且富有表达力。它有许多优势---它是动态类型的,不需要编译环节以及拥有一个巨大的能够提供强大框架、库和其他工具的生态系统。

  • WebAssembly 是一门低级的类汇编语言。它有一种紧凑的二进制格式,使其能够以接近原生性能的速度运行,并且为诸如 C++ 和 Rust 等拥有低级的内存模型语言提供了一个编译目标以便它们能够在浏览器上运行。另外,WebAssembly 不具备 DOM 操作能力。

  • JavaScript 和 WebAssembly 之间可以互相调用。

image.png

生成 WebAssembly

JavaScript 作为高级语言,我们可以直接编写。但是 WebAssembly 是汇编语言,那该如何产生呢?

1.直接在汇编层,编写或生成 WebAssembly 代码

虽然是汇编语言,写起来会十分麻烦,但也不是不可能。当然,我们很少会选择编写汇编。

2.使用工具编译成 WebAssembly 代码

既然不想直接写汇编语言,那我们通过工具编译高级语言。比如:使用 Emscripten 就可以把一个C/C++应用程序编译成 WebAssembly。Emscripten 能够将一段C/C++代码,编译出:

  • 一个.wasm模块
  • 用来加载和运行该模块的JavaScript”胶水“代码
  • 一个用来展示代码运行结果的HTML文档

image.png

3.使用文本格式,编写并生成 WebAssembly 代码

WebAssembly 有一种特殊的表示方式,那就是文本格式。你可以手动书写或者生成这种格式然后使用这些工具(WebAssemby text-to-binary tools)中的任何一个把它转换为二进制格式。

WebAssembly 文本格式文件通常被保存为.wat扩展名;有时.wast也被使用,它是说文件包含了额外的测试命令(断言等)并且它们不需要转换到.wasm中。

WebAssembly 文本格式文件的如下所示,现在看不懂也没事,之后会专门来介绍这种特殊的格式。

(module
  (func $i (import "imports" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
    call $i
  )
)

wasm 模块加载

对 WebAssembly 有了一些认知之后,接下来我们来实操一下如何在 html 页面中加载 wasm 模块。

wasm 模块加载过程

1.创建 wasm 模块

既然要加载 wasm 模块,那么首先得有一个现成的 wasm 模块,具体生成方式我们可以参考以上三种方式,此处我们准备了一个 simple.wasm 模块。

2.获取 wasm 资源

获取资源的方式和我们常规的获取方式一致,通过 XMLHttpRequest 或 Fetch,此处以 Fetch 为例,该函数返回一个可以解析为 Response 对象的 promise。接着使用 arrayBuffer 函数把响应转换为带类型数组,该函数返回一个可以解析为带类型数组的 promise。

3.wasm 模块实例化

此时,我们获取的是 wasm 模块,最后使用 WebAssembly.instantiate 函数进行实例化。

用代码表示如下:

fetch('module.wasm').then(response =>
  response.arrayBuffer()
).then(bytes =>
  WebAssembly.instantiate(bytes, importObject)
).then(results => {
  // Do something with the compiled results!
});

wasm 模块加载 demo

完成代码:wasm-learn

1.克隆代码到本地

git clone https://github.com/xindoucha/wasm-learn.git

2.在项目根目录启动服务

cd wasm-learn
npx http-server ./  --cors *

3.访问 index.html 页面

http://localhost:8080/index.html

4.查看结果 打开控制台,看到模块执行的结果:42。 在网络请求中可以看到对 simple.wasm 的请求。

Ps: 如果对 demo 中模块初始化的部分看不懂,那也没有关系,后续会详细介绍 wasm 模块的初始化。

总结

  • 1.WebAssembly 基本介绍
  • 2.JavaScript 和 WebAssembly 关系
  • 3.WebAssembly 生成方式
  • 4.Wasm 模块加载