什么是 WebAssembly

WebAssembly(以下简称 wasm)是一种新的编码方式,可以在现代的网络浏览器中运行。啥意思,就是除了 JavaScript(以下简称 js),还有一种语言可以在浏览器里面运行了!

对于网络平台而言,WebAssembly 具有巨大的意义——它提供了一条途径,以使得以各种语言编写的代码都可以以接近原生的速度在 Web 中运行。在这种情况下,以前无法以此方式运行的客户端软件都将可以运行在 Web 中。

编译支持 wasm 的 go

golang 的 release 版本(1.10)还不支持 wasm,但是在 github.com/golang/go 的 master 分支中已经包含 wasm 了,所以我们需要自己编译一个 go。

git clone https://github.com/golang/go /path/go
cd /path/go/src
./make.bash

/path/go/bin/目录下会生成编译后的 go:/path/go/bin/go

设置GOROOT/path/go/

创建自己的 wasm 程序

参见项目:https://github.com/chyroc/golang-wasm-example

index.html

index.html引入了wasm_exec.js(这个是从/path/go/misc/wasm/wasm_exec.js复制来过的)和加载 wasm 的 js(这个参见参见文档),总之这样我们的 wasm 就已经加载到我们的网页了

最后有一个显示数字的 span 块,和两个+-的 button,我们待会写的 wasm 代码就是操作这个 span 块的

<html>
<head>
    <title>加1减1</title>
    <script src="wasm_exec.js"></script>
    <script type="text/javascript">
        function fetchAndInstantiate(url, importObject) {
            return fetch(url).then(response =>
                    response.arrayBuffer()
            ).then(bytes =>
                    WebAssembly.instantiate(bytes, importObject)
            ).then(results =>
                    results.instance
            );
        }

        var go = new Go();
        var mod = fetchAndInstantiate("./example.wasm", go.importObject);
        window.onload = function () {
            mod.then(function (instance) {
                go.run(instance);
            });
        };
    </script>
</head>
<body>
<br>
<div>加1减1</div>
<br>
<button id="minus" type="button" disabled=true>-</button>
<span id="number">0</span>
<button id="plus" type="button" disabled=true>+</button>
</body>
</html>

wasm.go

第一行有 build tag:// +build js,wasm表示GOARCHwasmGOOSjs

然后syscall/js包提供了操作网页的接口:

js.Global.Get("document").Call("getElementById", "number")这几行获取到刚刚index.html中最后一个按钮和 span 块

全局变量number是我们最后操作的数字

然后对+-按钮添加事件监听,监听到按了+,就将number加 1;监听到按了-,就将number减 1;并渲染 span 块。

plus.Set("disabled", false)这两句会在页面 wasm 加载后将+-按钮 disable 状态去掉,变成可点击状态

最后使用select{}阻塞 go 程序不退出

// +build js,wasm

package main

import (
	"syscall/js"
	"strconv"
)

func main() {
	var numberDoc = js.Global.Get("document").Call("getElementById", "number")
	var plus = js.Global.Get("document").Call("getElementById", "plus")
	var minus = js.Global.Get("document").Call("getElementById", "minus")
	var number int

	plus.Call("addEventListener", "click", js.NewCallback(func(args []js.Value) {
		println("press +")
		number++
		numberDoc.Set("innerHTML", strconv.Itoa(number))
	}))

	minus.Call("addEventListener", "click", js.NewCallback(func(args []js.Value) {
		number--
		numberDoc.Set("innerHTML", strconv.Itoa(number))
		println("press -")
	}))

	plus.Set("disabled", false)
	minus.Set("disabled", false)

	select {}
}

程序效果请访问:https://chyroc.cn/golang-wasm-example/ 体验

参考