关于 Go 即将支持的 WebAssembly 的一些注意事项

themoonbear · 2018-07-14 12:06:09 · 4941 次点击 · 预计阅读时间 4 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-07-14 12:06:09 的文章,其中的信息可能已经有所发展或是发生改变。

这是一篇关于 webassembly 的即时记录,它的目的是给我做个备忘而不仅仅是如果使用它的教程。

即将发布的 Go 1.11 版本将支持 Wasm。@neelance 做了大部分的实施工作。对 wasm 的支持已经可以通过他在 github 上的工作分支进行测试。

这篇文章了解更多信息

工具链设置

要从 go 源码生产一个 wasm 文件,您需要从源码获取并为 go 工具集打补丁:

~ mkdir ~/gowasm
~ git clone https://go.googlesource.com/go ~/gowasm
~ cd ~/gowasm
~ git remote add neelance https://github.com/neelance/go
~ git fetch --all
~ git checkout wasm-wip
~ cd src
~ ./make.bash

然后使用这个版本的 Go,把 GOROOT 指到 ~/gowasm 并使用 ~/gowasm/bin/go 这个二进制文件。

第一个例子

按照惯例,让我们先来写个 “hello world”:

package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}

然后编译出文件并命名 example.wasm

GOROOT=~/gowasm GOARCH=wasm GOOS=js ~/gowasm/bin/go build -o example.wasm main.go

运行这个例子

这是官方文档的节选:

虽然有计划允许 WebAssembly 模块像 ES6 模块(...)那样加载,但目前 WebAssembly 必须由 JavaScript 加载并编译,对于基本的加载,有如下三个步骤:

  • 将 .wasm 字节转化为数组或 ArrayBuffer
  • 将字节编译为 WebAssemly.Module
  • 使用导入实例化的 WebAssembly.Module 以获取可调用的导出

幸运的是,Go 的作者已经提供了一个 Javascript 加载器(~/gowasm/misc/wasm/wasm_exec.js)来简化这个过程。它附带一个 HTML 文件,负责粘贴浏览器中所有的内容。

要实际运行我们的文件,需要将以下文件复制到一个目录中并作为 Web 服务启动:

~ mkdir ~/wasmtest
~ cp ~/gowasm/misc/wasm/wasm_exec.js ~/wasmtest
~ cp ~/gowasm/misc/wasm/wasm_exec.html ~/wasmtest/index.html
~ cp example.wasm ~/wasmtest

然后编辑这个 index.html 文件去运行正确的例子:

// ...
WebAssembly.instantiateStreaming(fetch("example.wasm"), go.importObject).then((result) => {
    mod = result.module;
    inst = result.instance;
    document.getElementById("runButton").disabled = false;
});
// ...

理论上,任何 web 服务都可以运行它,但是当我们试着用 caddy 运行它时遇到一个问题。这个 javascript 加载器需要服务发送这个 wasm 文件的正确 mime 类型给它。

这有一个快速的破解方法来运行我们的测试:为我们的 wasm 文件写个带有特殊处理的 Go 服务。

package main

import (
    "log"
    "net/http"
)

func wasmHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/wasm")
    http.ServeFile(w, r, "example.wasm")
}
func main() {
    mux := http.NewServeMux()
    mux.Handle("/", http.FileServer(http.Dir(".")))
    mux.HandleFunc("/example.wasm", wasmHandler)
    log.Fatal(http.ListenAndServe(":3000", mux))
}

注意 设置一个特殊的路由器来处理所有的 wasm 文件没什么大不了,如我所说,这是一个 POC,这篇文章只是关于它的附注。

然后使用go run server.go来启动服务,并打开浏览器访问 http://localhost:3000。

打开控制台看看!

和浏览器交互

让我们和世界互动。

解决 DOM 问题

syscall/js 包中包含允许通过 javascript API 与 DOM 交互的函数。要获取此包的文档,只需运行: GOROOT=~/gowasm godoc -http=:6060 然后用浏览器访问 http://localhost:6060/pkg/syscall/js/

让我们写个简单的 HTML 文件来显示一个输入框。然后从 webassembly,我们给这个元素绑定一个事件,并在监听到事件时触发一个动作。

编辑 index.html 并把代码放在 run 按钮下面:

    <button onClick="run();" id="runButton" disabled>Run</button>
    <input type="number" id="myText" value="" />
</body>

然后修改 Go 文件:

package main

import "fmt"

func main() {
     c := make(chan struct{}, 0)
     cb = js.NewCallback(func(args []js.Value) {
          move := js.Global.Get("document").Call("getElementById", "myText").Get("value").Int()
          fmt.Println(move)
      })
      js.Global.Get("document").Call("getElementById", "myText").Call("addEventListener", "input", cb)
      // The goal of the channel is to wait indefinitly
      // Otherwise, the main function ends and the wasm modules stops
      <-c
}

像以前一样编译文件并刷新浏览器……打开控制台然后输入一个数字……瞧瞧

暴露函数

这有点辣手……我没有找到任何简单的方法将一个 Go 函数暴露给 Javascript 生态系统。我们需要做的是在 Go 文件中创建一个 Callback 对象并指定到一个 Javascript 对象。

为得到返回结果,我们不能返回一个值给 callback 而是使用 Javascript 对象代替。

这是新的 Go 代码:

package main
import (
    "syscall/js"
)

func main() {
    c := make(chan struct{}, 0)
    add := func(i []js.Value) {
        js.Global.Set("output", js.ValueOf(i[0].Int()+i[1].Int()))
    }
    js.Global.Set("add", js.NewCallback(add))
    <-c
}

现在编译并运行代码。打开浏览器和控制台。

如果你输入 output 将返回 Object not found。现在您输入 add(2,3)output...应该得到5。

这不是很优雅的交互方式,但它按预期运行。

结论

Go 对 wasm 的支持刚刚开始,但正大力发展。许多功能现在都可运行。我甚至可以在浏览器运行一个完整的递归神经网络,这归功于 Gorgonia。我将稍后讲解这些。


via: https://blog.owulveryck.info/2018/06/08/some-notes-about-the-upcoming-webassembly-support-in-go.html

作者:Parikshit Agnihotry  译者:themoonbear  校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4941 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
4 回复  |  直到 2018-12-09 08:15:08
uinz
uinz · #1 · 7年之前

image.png

freeozyl80
freeozyl80 · #2 · 6年之前

Wasm decoding failed: expected magic word 00 61 73 6d, found cf fa ed fe @+0 . 请教下这个报错是什么情况导致的呢,按照教程来的啊

freeozyl80
freeozyl80 · #3 · 6年之前
freeozyl80freeozyl80 #2 回复

Wasm decoding failed: expected magic word 00 61 73 6d, found cf fa ed fe @+0 . 请教下这个报错是什么情况导致的呢,按照教程来的啊

我的锅,文件内容不对

themoonbear
themoonbear · #4 · 6年之前
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传