使用 WebAssembly 和 Go 进行开发:高级水平
WebAssembly (WASM) 为 Web 开发开辟了新的视野,允许执行用 JavaScript 以外的语言编写的高性能代码。Go (Golang) 凭借其 WASM 编译器和简单的并发模型,是创建复杂 Web 模块的绝佳选择。在本文中,我们将探讨 Go 和 WebAssembly 集成的高级技术:内存操作、复杂数据结构的传递、与 JavaScript 的双向通信以及性能优化。
1. 在 Go 和 JavaScript 之间传递复杂数据结构
通过 syscall/js 进行基本的数字和字符串传递很简单,但对于实际应用来说还不够。高级方法涉及将复杂结构(切片、映射、自定义类型)序列化为 JSON 或二进制格式。
通过 JSON 进行序列化
最简单且通用的方法是使用 encoding/json。您导出一个函数,该函数接收 JSON 字符串,将其反序列化为 Go 结构,进行处理,然后将结果以 JSON 形式返回。
// main.gopackage main
import ( "encoding/json" "syscall/js")
type InputData struct { Name string `json:"name"` Value float64 `json:"value"`}
type OutputData struct { Greeting string `json:"greeting"` Square float64 `json:"square"`}
func processData(this js.Value, args []js.Value) interface{} { if len(args) < 1 { return "Error: expected JSON string" } inputJSON := args[0].String()
var input InputData if err := json.Unmarshal([]byte(inputJSON), &input); err != nil { return "Error: " + err.Error() }
output := OutputData{ Greeting: "Hello, " + input.Name, Square: input.Value * input.Value, }
resultJSON, _ := json.Marshal(output) return string(resultJSON)}
func main() { c := make(chan struct{}, 0) js.Global().Set("processData", js.FuncOf(processData)) <-c}直接内存操作(二进制协议)
为了获得最大性能,可以通过 WASM 线性内存传递数据。您导出用于从内存写入/读取的函数,并将指针(以整数形式)传递给 JavaScript。这种方法更复杂,但可以避免 JSON 解析的开销。
// 导出用于写入内存的函数//go:wasmexport allocateMemoryfunc allocateMemory(size int) uintptr { // 使用 C.malloc 或自定义分配器 // 返回指针}
//go:wasmexport getMemoryBufferfunc getMemoryBuffer() uintptr { // 返回缓冲区起始地址}在 JavaScript 中,您使用 WebAssembly.Memory 和 Uint8Array 在获取的地址上进行读取/写入。
2. 从 Go 调用 JavaScript 并操作 DOM
syscall/js 包允许调用任何 JavaScript 函数并操作 DOM。高级水平涉及创建响应式界面和处理事件。
创建和更新 DOM 元素
func appendDiv(this js.Value, args []js.Value) interface{} { document := js.Global().Get("document") body := document.Get("body")
newDiv := document.Call("createElement", "div") newDiv.Set("innerHTML", "<h2>Hello from Go WASM!</h2>") newDiv.Get("style").Set("color", "blue") body.Call("appendChild", newDiv) return nil}
func main() { js.Global().Set("appendDiv", js.FuncOf(appendDiv)) select {}}在 Go 端处理事件
func setupClickHandler(this js.Value, args []js.Value) interface{} { document := js.Global().Get("document") button := document.Call("getElementById", "myButton")
clickCallback := js.FuncOf(func(this js.Value, args []js