什么是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/