17611538698
webmaster@21cto.com

WebAssembly 技术初探

前端 0 1051 2023-08-04 07:11:31

图片

1 WebAssembly 是什么?

一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果
W3C WebAssembly Community Group 开发的一项网络标准,对于浏览器而言,WebAssembly 提供了一条途径,让各种语言编写的代码以接近原生的速度在 Web 中运行。在这种情况下,以前无法以此方式运行的客户端软件等都将可以运行在 Web 中。
WebAssembly 设计之初就决定和 JavaScript 一起协同运行 —— 通过 JavaScript 中的 WebAssembly API,可以把 WebAssembly 模块加载到一个 JavaScript 应用中并且在两者之间互相调用。这样可以在同一个应用中使用 WebAssembly 的高性能及 JavaScript 的高灵活性。

2 为什么需要 WebAssembly?

众所周知 JavaScript 是解释型语言,相比于编译型语言需要在运行时转换,所以解释型语言的执行速度要慢于编译型语言。
编译型语言和解释型语言代码执行的具体流程如下:
图片
因为解释型语言每次执行都需要把源码转换一次才能执行,而转换过程非常耗费时间和性能,也就导致在 JavaScript 背景下,web 无法执行一些高性能应用,如图片剪辑、视频剪辑、3D 游戏等。
根据 MDN 的定义,WebAssembly 是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++ 等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。

3 WebAssembly 的工作原理

WebAssembly 不被解释,而是由开发者提前编译为 WebAssembly 二进制格式,如下图所示。由于变量类型都是预知的,因此浏览器加载 WebAssembly 文件时,JavaScript 引擎无须监测代码。它可以简单地将这段代码的二进制格式编译为机器码。
图片
如果将每种编程语言都直接编译为机器码的各个版本,那么效率会很低。编译器中称为前端的部分会将所编写的代码编译为一种中间表示 (intermediate representation,IR)。创建好 IR 代码后,编译器的后端部分会接收 IR 代码,对其进行优化,然后将其转换为所需要的机器码。
图片
由于浏览器可以在若干不同的处理器 (比如桌面计算机、智能手机和平板设备) 上运行,因此为每个可能的处理器发布一个 WebAssembly 代码的编译后版本会非常繁复。替代方法即取得 IR 代码,并通过一个专门的编译器来运行,这个编译器将 IR 代码转换为一种专用字节码并放入后缀为.wasm 的文件中。此时 wasm 文件中的字节码还不是机器码,它只是支持 WebAssembly 的浏览器能够理解的一组虚拟指令。当加载到支持 WebAssembly 的浏览器中时,浏览器会验证这个文件的合法性,然后这些字节码会继续编译为浏览器所运行的设备上的机器码。如下图
图片
图片
WebAssembly 被设计为 JavaScript 的一个组件,不是它的替代品。虽然有些开发者试图只用 WebAssembly 来创建整个网站,但这不是普遍情况。一般情况 JavaScript 仍然是更好的选择。

4 WebAssembly 模块内部

图片
模块中不同段的含义说明:
图片图片
编译器负责生成 WebAssembly 模块的段,并将它们按照适当顺序放置。
所有的段都是可选的,因此可能存在空模块。
如果指定了已知段,那么它们只能出现一次并且要按照特定顺序出现。
自定义段可以放置在已知段之前、之间或之后,用于指定不适用已知段的数据。

5 哪些语言可用来创建 WebAssembly 模块?

现在 WebAssembly 的最小可行性版本(Minimum Viable Product,MVP)还没有垃圾回收(garbage collection,GC),他限制了一些语言的使用。GC 作为一种后 MVP 功能正在开发中,实现之前,有几种语言正在试验 WebAssembly 支持,方式是将自己的 VM 编译到 WebAssembly,或者在某些情况下将自己的垃圾回收器包含进去。
以下语言正在试验或已经完成 WebAssembly 支持:
  • C 和 C++

  • Rust 正致力于成为 WebAssembly 的首选编程语言。

  • AssemblyScript 是一种新编译器,它用来将 TypeScript 转换为 WebAssembly。

  • TeaVM 是一个将 Java 转译到 JavaScript 的工具,现在也可以生成 WebAssembly 了。

  • Go 1.11 为 WebAssembly 增加了一个试验性项目,其编译后的 WebAssembly 模块包含一个垃圾回收器。

  • Pyodide 是 Python 的一个项目,其中包含了 Python 科学栈的核心包:Numpy、Pandas 和 matplotlib。

  • Blazor 是微软的实验性项目,用于将 C# 引入 WebAssembly。

更多列表关注 github: WebAssembly 支持列表
相关案例:
TeaVM:它可以将 JVM 字节码翻译成 JavaScript 和 WebAssembly
我们有一段时间后端开始做一些前端开发,但是结果有时并不尽如人意,关键就在于我们的后端开发人员对前端无论是框架还是语法还是规范,都不是非常了解。这是在所难免的,但是因为业务需要又不得不做。
TeaVM 就为我们这种情况提供了一种解决方案,我们的后端开发人员依然使用自己熟悉的语言(java)进行开发。功能开发完成后再将_.class 或_.jar 文件通过 TeaVM 编译成 wasm 或 JavaScript 供浏览器加载调用。
git:https://github.com/konsoletyper/teavm
官网:https://teavm.org/

6 WebAssembly 可以用在哪?

目前大多数浏览器厂商都已经支持 WebAssembly,包括 Chrome、Edge、Firefox 和 Safari。移动端 Web 浏览器也同样支持。Node.js 也从版本 8 开始支持。
WebAssembly 不是 JavaScript 的替代品,而是它的一个补充,有些情况下 WebAssembly 是更好的选择,有些情况下使用 JavaScript 会是一个更优的方案。与 JavaScript 在同一个 VM 运行可让两种技术相辅相成。
WebAssembly 为非 JavaScript 的开发者提供了一个新的道路,帮助他们在 web 中使用自己编写的代码。也让不了解 C 或 C++ 等语言的 web 开发者可与访问更新、更快的库。个人理解 WebAssembly 也可用来优化某些库的执行速度。

6.1 一些使用 webAssembly 的案例

Figma — 基于浏览器的多人实时协作 UI 设计工具:https://www.figma.com/
Google Earth https://earth.google.com/ - 17 年开始支持在 FireFox 打开,主要依赖 webAssembly。之前使用 Native Client 导致只能在 chrome 中运行
Magnum — 跨平台的 OpenGL 图形引擎 https://github.com/mosra/magnum
Egret Engine - 一款 HTML5 游戏引擎 https://github.com/egret-labs/egret-core/
Web-DSP — 使用浏览器就能即时制作多媒体影音特效 https://github.com/shamadee/web-dsp

7 WebAssembly 怎么用?

7.1 得到 wasm 文件手动引入

var importObject = {
imports: {
imported_func: function(arg) {
console.log(arg);
}
}
};
// 输出 42
fetch('simple.wasm')
.then(res =>
res.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
results.instance.exports.exported_func();
});

图片

图片

7.2 得到编译好的 npm 包引入执行

// alert(`Hello, ${name}`)
const js = import("./node_modules/@jdl/hello-wasm/hello_wasm.js");
js.then(js => {
js.greet("WebAssembly");
});

图片
以下为 hello_wasm.js 文件编译前源码
// rust
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}

本文从为什么需要 WebAssembly、WebAssembly 的工作原理、哪些语言可用来创建 WebAssembly 模块、WebAssembly 可以用在哪里 以及 怎么使用 几方面简要介绍了 webAssembly。如果之前没有了解过 webAssembly,可以做一些简要的了解。

参考文献
《WebAssembly 实战》 —- C. 杰勒德・加伦特
编译 Rust 为 WebAssembly - WebAssembly | MDN

如果本文对你有用,欢迎点赞、转发。

作者:潘维高

来源:京东云开发者

评论