一. 前言
WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。
二. 快速上手
1. 用go写一个hello world
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, WebAssembly!")
}
2. 将go文件编译成wasm文件
GOOS=js GOARCH=wasm go build -o static/main.wasm
3. 拷贝出wasm_exec.js
该文件为go的wasm的js支持文件
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" static
4. html文件调用wasm文件
<script src="static/wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("static/main.wasm"), go.importObject)
.then((result) => go.run(result.instance))
</script>
5. 验证调用
浏览器加载html文件,f12打开控制台,可以看到wasm的打印消息。
三. go与js的类型转换
1. 类型映射
| Go | JavaScript |
| ---------------------- | ---------------------- |
| js.Value | [its value] |
| js.Func | function |
| nil | null |
| bool | boolean |
| integers and floats | number |
| string | string |
| []interface{} | new array |
| map[string]interface{} | new object |
如上为官方给出的go与js的类型映射表。
比如在go中调用js函数,参数为array
,那么就可以直接将go的[]interface{}
类型的变量作为参数使用。
2. 函数转换数组
syscall/js
提供了两个函数:
- CopyBytesToGo:
func CopyBytesToGo(dst []byte, src Value) int
- CopyBytesToJS:
func CopyBytesToJS(dst Value, src []byte) int
两者对于go而言,类型都是[]byte
,但是对于js而言,需要Uint8Array
或者Uint8ClampedArray
类型,否则就会报错。
那么,如何在go中生成一个Uint8Array
或者Uint8ClampedArray
类型的变量呢?官方的类型映射表也没有啊…那么就看下一步。
3. 其余类型
对于非官方类型映射表内的类型,和官方提供的两个数据类型转换之外的类型,可以通过一种通用的方式来生成,以上一步的Uint8Array
为例:
js.Global().Get("Uint8Array").New(<length>)
实际使用案例:
// goData []byte{...}
jsData := js.Global().Get("Uint8Array").New(len(goData))
js.CopyBytesToJS(jsData, goData)
那么,比如js中的Date
类型:
dateConstructor := js.Global().Get("Date")
dateConstructor.New("2020-10-01")
4. 极端情况
好吧,还有最后一个方案,如果遇到极端情况,上述方案都无法解决,那么请转换成字符串吧!让go和js用各自的方法分别处理一波,得到自己想要的结果或者给出各自想给的数据。
四. js调用go函数
此处需要在go中引入syscall/js,以实现js相关的操作。
1. 注册go函数
将go的函数注册为js的函数,由js来进行调用。
package main
import "syscall/js"
func handleCount(this js.Value, args []js.Value) interface{
} {
count := args[0].Int()
return js.ValueOf(count + 1)
}
func main() {
done := make(chan string, 0)
js.Global().Set("HandleEvent", js.FuncOf(handleEvent))
<-done
}
js.Func()
接受一个函数类型作为其参数,该函数的定义是固定的:
func(this Value, args []Value) interface{
}
// this 即 JavaScript 中的 this
// args 是在 JavaScript 中调用该函数的参数列表。
// 返回值需用 js.ValueOf 映射成 JavaScript 的值
js.ValueOf返回作为js的值:
| Go | JavaScript |
| ---------------------- | ---------------------- |
| js.Value | [its value] |
| js.Func | function