WebAssembly
是基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为编程语言的可移植编译目标,支持客户端和服务器应用程序在 Web 上部署。官网:https://webassembly.org
TypeScript
ts -> wasm
安装一个转换工具:AssemblyScript 使用 Binaryen 将 TypeScript 的变体(基本上是带有类型的 JavaScript)编译为 WebAssembly。它生成精益而平均的 WebAssembly 模块,同时只需安装 npm。
bash
npm i assemblyscript
同时在package.json
中添加脚本:
- asc :编译 WebAssembly
- 指定源文件:
src/assembly/index.ts
(这里先写一个简单的ts文件测试下,就不贴代码了) --target release
:指定目标为 release
json
{
"scripts": {
"ascbuild:release": "asc src/assembly/index.ts --target release"
}
}
同时还需要添加asconfig.json
文件:
targets.release
:表示一个名为 release 的编译目标,通常用于生产环境,会开启优化outFile
:编译后的 wasm 二进制文件textFile
:编译后的 wat 文本格式sourceMap
:是否生成源映射文件options.bindings
:这个选项控制是否生成 JavaScript 绑定文件esm
:生成 es 模块es5
:传统 IIFE 格式(较少用)false
:不生成绑定文件
json
{
"targets": {
"release": {
"outFile": "build/assembly/index.wasm",
"textFile": "build/assembly/index.wat",
"sourceMap": true
}
},
"options": {
"bindings": "esm"
}
}
至此:执行npm run ascbuild:release
即可编译出 wasm 文件。
use wasm by ts in html
然后再wasm文件同级下添加一个release.js
文件,用来作为加载wasm文件的入口:
- 需要注意的点:在编译之前的ts文件中,方法要用function,用箭头函数在index.html当中识别不出来函数
- imports环境当中要补一个abort
语法说明:await (async url => { ... })(new URL('index.wasm', import.meta.url));
- 这是一个立即调用函数
- 将 URL 对象作为参数传递给异步函数
- 异步函数内部通过 url 参数访问这个 URL 对象
补充:为什么要使用立即调用函数?
- 作用域隔离:url 只在这个函数作用域内可用,不会污染全局作用域
- 模块化和封装性:立即执行函数将整个初始化逻辑封装在一起,提高代码内聚
- 控制执行时机:
- URL 创建和模块加载是原子操作
- 在模块导入时立即开始执行,而不是依赖外部变量
- 避免了可能出现的时序问题
- 函数式编程
- 避免变量提升问题
- 性能:立即执行函数在模块加载时一次性完成所有工作,避免了额外的变量声明和可能的重复计算
js
async function instantiate(module, imports = {}) {
const {exports} = await WebAssembly.instantiate(module, imports);
return exports;
};
/**
* 需要做用户检测(分浏览器和node环境)
* 浏览器环境:WebAssembly.compileStreaming
* node环境:WebAssembly.compile
* */
const imports = {
env: {
memory: new WebAssembly.Memory({initial: 256}), // 通用的函数占位符
abort: () => {
}
}
};
export const {
add, memory
} = await (async url => instantiate(await (async () => {
const isNode = typeof process !== 'undefined' && process.version !== null;
console.log('isNode =====', isNode);
if (isNode) {
const fs = await import('node:fs/promises');
return await globalThis.WebAssembly.compile(await fs.readFile(url));
} else {
return await globalThis.WebAssembly.compileStreaming(globalThis.fetch(url));
}
})(), imports))(new URL('index.wasm', import.meta.url));
最后在html当中引入这个js文件进行使用
html
<script type="module">
import {add, memory} from './release.js';
console.log('add', add, add(10, 20));
console.log('memory', memory);
</script>
安装npm i http-server
,执行命令http-server -p 8080
性能分析
利用performance API分析wasm性能,同时把编译前的ts文件改回js放在html当中进行分析性能进行比较
js
performance.mark('wasm_start');
console.log('add', add(10, 20));
performance.mark('wasm_end');
performance.measure('wasm_time', 'wasm_start', 'wasm_end');
console.log('performance', performance.getEntriesByName('wasm_time'));