自Netscape启动JavaScript以来,有些开发人员喜欢它,而其他开发人员则不喜欢。
无论您站在哪一方,我都可以同意如果浏览器支持更多编程语言。
这是WebAssembly的承诺:提供任何编程语言可以编译为。
过去的尝试
在Web的早期,尝试使用Java Applet和Microsoft ActiveX进行扩展。但是两者都受到安全问题的困扰,并最终下降了。问题是他们像本机代码一样执行 - 没有访问控件 - 它变成了巨大的攻击表面。
后来,Macromedia Flash和Silverlight取得了一些成功,但最终遇到了同样的悲剧命运。他们俩都缺乏开放标准,这使浏览器和OS供应商很难支持。
什么是WebAssembly?
WebAssembly (aka wasm)是open standard字节代码格式,可在所有浏览器中起作用。这是一种低级二进制格式和执行引擎,从概念上类似于Oracle的JVM或Microsoft的CLR。
它是从头设计的,以托管和安全。它无法访问机器的内存或硬盘驱动器。只有主机可以决定要暴露的API。
WASM是一种便携式格式,因此可以支持许多编程语言。认为Rust,Ruby,Python甚至JavaScript都可以编译为WASM字节代码。
尽管它最初是为了瞄准浏览器而设计的,但也可以在浏览器之外运行。
它可以在服务器,云,物联网/硬件设备上运行,也可以使用插件系统。
写作
有几种创建.wasm
文件的方法:
- 用手写它。 (不建议)
- 用Wasm Text Format。
- 使用更高级别的语言,例如汇编,Rust,Ruby等。然后进行编译。
我将向您展示一些示例:
什么是什么?
WASM规范概述了一种基于文本的格式,用于定义称为WAT(WASM文本格式)的WASM模块。它使用类似于lisp或clojure的S-主张。
这是一个基本模块的样子:
; define a module
(module
; define a function called "add"
; it takes 2 params:
; - $a is a 32-bit integer
; - $b is a 32-bit integer
; it returns an 32-bit integer
(fun add (param $a i32) (param $b i32) (result $i32)
; load param $a onto the stack
local.get $a
; load param $b onto the stack
local.get $b
; perform 32-bit integer "add" operation
i32.add
; the last value on the stack is returned
; which is the result of the `i32.add`
)
)
可以使用WebAssembly Toolkit CLI tools的一部分来将.wat
文件编译为.wasm
:
# outputs example.wasm
> wat2wasm example.wat
现在可以从任何主机执行.wasm
。它甚至可以使用wasmtime
:
从命令行执行
# invoke "add" function, and pass args 1,2
> wasmtime example.wasm --invoke add 1 2
3
汇编
还有一种称为AssemblyScript的高级语言。就像WebAssembly的打字稿一样。
例如,上一节的add()
函数可以重新编写为assemblyscript:
// in add.ts
export function add(a: u32, b: u32): u32 {
return a + b;
}
如您所见,它更可读。
要编译它,使用asc
:
pnpm install -D assemblyscript
pnpm run asc add.ts --outFile=math.wasm
比较格式
要将汇编与WAT进行比较,我构建了一个方便的工具:
https://assemblyscript-play.vercel.app
您也可以使用Cli wasm2wat
比较格式:
# outputs .wat format
wasm2wat math.wasm
运行时执行
就像有很多编译WASM的方法一样,也有很多方法可以执行它。
在浏览器中使用WebAssembly
要在浏览器中使用WebAssembly API,请首先加载组件:
// fetch .wasm file
const response = fetch('/path/to/some.wasm')
// instantiate module with streaming
const module = WebAssembly.instantiateStreaming(response)
然后,调用导出功能之一:
const result = module.instance.exports.add(1, 2)
也可以将可选的API传递到模块中:
// fetch .wasm file
const response = fetch('/path/to/some.wasm')
// instantiate module and pass an api
const module = WebAssembly.instantiateStreaming(response, {
imports: {
// share console.log
log: console.log
}
})
在服务器上使用WebAssembly
WebAssemblies也可以在服务器上执行。 API实际上与浏览器相同。
唯一的区别是,可以使用fs.readFile()
:
从磁盘上读取它,而不是使用URL从服务器获取.wasm
。
import fs from 'fs'
// read .wasm file
const response = await fs.promises.readFile('/path/to/some.wasm')
// instantiate module with streaming
const module = WebAssembly.instantiate(response)
然后,像我们在浏览器中所做的那样,调用导出的功能之一:
const result = module.instance.exports.add(1, 2)
也可以从许多其他语言中执行此操作。例如rust,ruby,python或来自CLI。
在云中使用WebAssembly
WASM的另一个大用例是云。
它比JavaScript云功能具有一些优势:
-
没有冷启动:主机只需要加载
.wasm
文件而不是完整的应用程序。典型的JS应用程序有许多要加载的文件,这需要很长时间。 - 更快地部署:所有被上传的都是简单的二进制文件。
- 多面体托管:编译为WASM的所有语言都可以部署到云中,而无需任何特殊的运行时。
- 快照:可以快照执行。例如,可以在初始化过程中进行昂贵工作的应用程序可以快照。然后将来的请求可以从快照开始,消除了昂贵的启动时间。
云中WASM的一个很好的例子是Fermyon。就像aws lambda,但对于WebAssembly。
要使用Fermyon,请安装他们的Cli spin。
然后创建一个新项目:
# create a new spin project
# template is "http-js"
# project name is "spin-example"
spin new http-js spin-example
cd spin-example
# install dependencies
npm install
然后在src/index.js
中定义一个处理程序:
const encoder = new TextEncoder()
export async function handleRequest(request) {
return {
status: 200,
headers: { "content-type": "text/plain" },
body: encoder.encode("Hello World").buffer
}
}
以开发模式运行:
spin watch
要部署到云,运行spin deploy
:
spin deploy
请注意该部署是如何即时的?
陷入困境
仍然有几个wasm的粗糙边缘:
- WebAssembly仍然是新的,并且正在积极发展。尽管它正在迅速改善。
- 某些编程语言尚不可用。
- WASM没有基本数据类型,例如字符串或标准库。这是设计。预计语言将提供自己的标准库。
- 因为“标准库”需要生活在您的
.wasm
内部,所以它可以使文件大小大。
其中大多数将随时间解决。
未来
在过去的几年中,WebAssembly取得了很多进步。
最终,所有语言都将具有托管目标和运行时间(如果还没有)。这将使所有语言都能在浏览器,服务器甚至硬件中运行。
它也可能会带来专为WebAssembly-Instrest World设计的新型编程语言。