什么是WebAssembly?
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.
让我们用tinygo来写一个WebAssembly的模块,然后用js调用吧。首先下载TINYGO:(https://tinygo.org/,请下载对应版本的二进制文件)。然后将bin/tinygo(or tinygo.exe)加入到PATH路径。
然后先创建一个main.go文件(文件名必须是main.go,因为js里有用到文件名),文件内容如下:
package main
// This calls a JS function from Go.
func main() {
println("Go: Hello World") // expecting 5
println("Go: add(2+3):", add(2, 3)) // expecting 5
println("Go: multiply(2*3):", multiply(2, 3)) // expecting 6
}
// This function is imported from JavaScript, as it doesn't define a body.
// You should define a function named 'main.add' in the WebAssembly 'env'
// module from JavaScript.
func add(x, y int) int
// This function is exported to JavaScript, so can be called using
// exports.multiply() in JavaScript.
//go:export multiply
func multiply(x, y int) int {
return x * y;
}
然后用tinygo编译这个go文件,生成wasm文件。
目前最新版的tinygo是0.12.0,最高只支持golang 1.13(目前最新版本的golang是1.14),编译的命令中文件名必须是main.go,不能写”./main.go”
tinygo build -o wasm.wasm -target wasm main.go
Error: tinygo build -o wasm.wasm -target wasm ./main.go
然后用go写一个简单的文件服务器(用于启动http服务器,因为wasm加载必须在http/https的环境下),注意对于wasm文件,要设置Content-Type为application/wasm
package main
import (
"log"
"net/http"
"strings"
)
const dir = "./html"
func main() {
fs := http.FileServer(http.Dir(dir))
log.Print("Serving " + dir + " on http://localhost:8080")
http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
resp.Header().Add("Cache-Control", "no-cache")
if strings.HasSuffix(req.URL.Path, ".wasm") {
resp.Header().Set("Content-Type", "application/wasm")
}
fs.ServeHTTP(resp, req)
}))}
然后在服务器目录下创建html目录,目录里新建index.html文件(注意js代码中设置env的代码):
<html>
<head>
<title>WebAssembly</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script type="text/javascript">
const go = new Go(); // Defined in wasm_exec.js
const WASM_URL = 'wasm.wasm';
go.importObject.env['main.go.add'] = function (x, y) {
return x + y
};
var wasm;
console.log("'instantiateStreaming' in WebAssembly => " + ('instantiateStreaming' in WebAssembly ? "Y" : "N"));
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
console.log('JS: multiplied two numbers:', wasm.exports.multiply(5, 3));
}).catch((err) => {
console.error(err);
});
} else {
fetch(WASM_URL).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
})
)
}
</script>
</body>
</html>
index.html里面引用的wasm_exec.js文件就在tinygo下载包里,wasm.wasm就是我们刚刚编译生成的文件。
浏览器里打开这个网页 http://localhost:8080/index.html,发现控制台就能输出:
23:25:58.955 ‘instantiateStreaming’ in WebAssembly => Y
23:25:58.964 Go: Hello World
23:25:58.964 Go: add(2+3): 5
23:25:58.964 Go: multiply(2*3): 6
23:25:58.964 JS: multiplied two numbers: 15
23:25:58.966 Fetch finished loading: GET “http://localhost:8080/wasm.wasm”.
References:
https://webassembly.org/
https://tinygo.org/