2017年2月28日,四大浏览器,IE,FF,Chrome,Safari宣布达成共识,即 WebAssembly 的 MVP (最小化可行产品)已经完成。大约一周后,Firefox会默认打开 WebAssembly 支持,而Chrome则在第二周开始。它也可用于预览版本的Edge和Safari。如今过去两年多了,发展还是挺迅速的。
使用WebAssembly,可以更快地在 web 应用上运行代码。这里有 几个 WebAssembly 代码运行速度比 JavaScript 高效的原因。
文件加载 – WebAssembly 文件体积更小,所以下载速度更快。
解析 – 解码 WebAssembly 比解析 JavaScript 要快
编译和优化 – 编译和优化所需的时间较少,因为在将文件推送到服务器之前已经进行了更多优化,JavaScript 需要为动态类型多次编译代码
重新优化 – WebAssembly 代码不需要重新优化,因为编译器有足够的信息可以在第一次运行时获得正确的代码
执行 – 执行可以更快,WebAssembly 指令更接近机器码
垃圾回收 – 目前 WebAssembly 不直接支持垃圾回收,垃圾回收都是手动控制的,所以比自动垃圾回收效率更高。
目前浏览器中的 MVP(最小化可行产品) 已经很快了。在接下来的几年里,随着浏览器的发展和新功能的增加,它将在未来几年内变得更快。没有人可以肯定地说,这些性能改进可以实现什么样的应用。但是,如果过去有任何迹象,我们可以期待惊奇。
说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。
在我的个人理解上,WebAssembly并没有要替代JavaScript,一统天下的意思。我总结下来就两个点。
要进行这个实际操作,你需要安装上文提到过的编译器Emscripten,然后按照这个步骤去安装。以下的步骤都默认为你已经安装了Emscripten。
进入到你的emscripten安装目录,执行以下代码。
source emsdk/emsdk_env.sh
用C实现一个求和文件test.c
,如下。
int add(int a, int b) {
return a + b;
}
在同样的目录下执行如下代码。
emcc test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o test.wasm
emcc
就是Emscripten编译器,test.c
是我们的输入文件,-Os
表示这次编译需要优化,-s WASM=1
表示输出wasm的文件,因为默认的是输出asm.js,-s SIDE_MODULE=1
表示就只要这一个模块,不要给我其他乱七八糟的代码,-o test.wasm
是我们的输出文件。
编译成功之后,当前目录下就会生成test.wasm
。
新建一个js文件test.js
。代码如下。
const fs = require('fs');
let src = new Uint8Array(fs.readFileSync('./test.wasm'));
const env = {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({
initial: 256
}),
table: new WebAssembly.Table({
initial: 2,
element: 'anyfunc'
}),
abort: () => {throw 'abort';}
}
WebAssembly.instantiate(src, {env: env})
.then(result => {
console.log(result.instance.exports._add(20, 89));
})
.catch(e => console.log(e));
运行以下代码。
node test.js
然后就可以看到输出的结果109了。
直接用fetch的方式。大概的调用方式如下。
const fibonacciUrl = './fibonacci.wasm';
const {_fibonacci} = await this.getExportFunction(fibonacciUrl);
而getExportFunction
具体代码如下。
getExportFunction = async (url) => {
const env = {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({
initial: 256
}),
table: new WebAssembly.Table({
initial: 2,
element: 'anyfunc'
})
};
const instance = await fetch(url).then((response) => {
return response.arrayBuffer();
}).then((bytes) => {
return WebAssembly.instantiate(bytes, {env: env})
}).then((instance) => {
return instance.instance.exports;
});
return instance;
};
先通过Import的方式来引进依赖。
import wasmC from './add.c';
然后进行调用。具体的方式如下。
wasmC({
'global': {},
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'})
}
}).then(result => {
const exports = result.instance.exports;
const add = exports._add;
const fibonacci = exports._fibonacci;
console.log('C return value was', add(2, 5643));
console.log('Fibonacci', fibonacci(2));
});
在这里能够举的例子还是很多,比如AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack等等。拿其中几个来简单说一下。
这是一个用于画图的软件,在很长的一段时间是没有Web的版本的,原因有两个,其一,是Web的性能的确不能满足他们的需求。其二,在WebAssembly没有面世之前,AutoCAD是用C++实现的,要将其搬到Web上,就意味着要重写他们所有的代码,这代价十分的巨大。
而在WebAssembly面世之后,AutoCAD得以利用编译器,将其沉淀了30多年的代码直接编译成WebAssembly,同时性能基于之前的普通Web应用得到了很大的提升。正是这些原因,得以让AutoCAD将其应用从Desktop搬到Web中。
Google Earth也就是谷歌地球,因为需要展示很多3D的图像,对性能要求十分高,所以采取了一些Native的技术。最初的时候就连Google Chrome浏览器都不支持Web的版本,需要单独下载Google Earth的Destop应用。而在WebAssembly之后呢,谷歌地球推出了Web的版本。而据说下一个可以运行谷歌地球的浏览器是FireFox。
这里给两个油管的链接自己体验一下,大家注意上网的方式。