乐趣区

【译】Go和WebAssembly:在浏览器中运行Go程序

在过去很长一段时间里,Javascript 是 Web 开发人员中的通用语言。如果你想写一个稳定成熟的 Web 应用程序,用 javascript 几乎是唯一的方法。
WebAssembly(也称为 wasm)将很快改变这种情况。使用 WebAssembly 可以用任何语言编写 Web 应用程序。在本文中,我们将了解如何编写 Go 程序并使用 wasm 在浏览器中运行它们。
但首先,什么是 WebAssembly
webassembly.org 将其定义为“基于堆栈的虚拟机的二进制指令格式”。这是一个很好的定义,但让我们将其分解为我们可以轻松理解的内容。
从本质上讲,wasm 是一种二进制格式; 就像 ELF,Mach 和 PE 一样。唯一的区别是它适用于虚拟编译目标,而不是实际的物理机器。为何虚拟?因为不同于 C/C++ 二进制文件,wasm 二进制文件不针对特定平台。因此,您可以在 Linux,Windows 和 Mac 中使用相同的二进制文件而无需进行任何更改。因此,我们需要另一个“代理”,它将二进制文件中的 wasm 指令转换为特定于平台的指令并运行它们。通常,这个“代理”是一个浏览器,但从理论上讲,它也可以是其他任何东西。
这为我们提供了一个通用的编译目标,可以使用我们选择的任何编程语言构建 Web 应用程序!只要我们编译为 wasm 格式,我们就不必担心目标平台。就像我们编写一个 Web 应用程序一样,但是现在我们有了用我们选择的任何语言编写它的优势。
你好 WASM
让我们从一个简单的“hello world”程序开始,但是要确保您的 Go 版本至少为 1.11。我们可以这样写:
package main

import (
“fmt”
)

func main() {
fmt.Println(“hello wasm”)
}
保存为 test.go。看起来像是一个普通的 Go 程序。现在让我们将它编译为 wasm 平台程序。我们需要设置 GOOS 和 GOARCH。
$GOOS=js GOARCH=wasm go build -o test.wasm test.go
现在我们生成了 wasm 二进制文件。但与原生系统不同,我们需要在浏览器中运行它。为此,还需要再做一点工作来实现这一目标:

Web 服务器来运行应用
一个 index.html 文件,其中包含加载 wasm 二进制文件所需的一些 js 代码。
还有一个 js 文件,它作为浏览器和我们的 wasm 二进制文件之间的通信接口。

我喜欢把它想象成制作 The PowerPuff Girls 所需要的东西。

然后,BOOM,我们有了一个 WebAssembly 应用程序!
现在 Go 目录中已经包含了 html 和 js 文件,因此我们将其复制过来。
$cp “$(go env GOROOT)/misc/wasm/wasm_exec.js” .
$cp “$(go env GOROOT)/misc/wasm/wasm_exec.html” .
$# we rename the html file to index.html for convenience.
$mv wasm_exec.html index.html
$ls -l
total 8960
-rw-r–r– 1 agniva agniva 1258 Dec 6 12:16 index.html
-rwxrwxr-x 1 agniva agniva 6721905 Sep 24 12:28 serve
-rw-rw-r– 1 agniva agniva 76 Dec 6 12:08 test.go
-rwxrwxr-x 1 agniva agniva 2425246 Dec 6 12:09 test.wasm
-rw-r–r– 1 agniva agniva 11905 Dec 6 12:16 wasm_exec.js
serve 是 Go 二进制文件,是一个 Web 服务器。但几乎任何 Web 服务器都可以。(译者注: 原文并没有提供 serve 二进制文件的源代码,相信聪明的你一定知道怎样编写。)
一旦运行它,并打开浏览器。可以看到一个 Run 按钮,点击它,将执行我们的应用程序。然后我们点击它并检查控制台:

真牛,我们刚刚在 Go 中编写了一个程序并在浏览器中运行它。
到现在为止一切顺利。但这是一个简单的“hello world”程序。真实的 Web 应用程序需要与 DOM 交互。我们需要响应按钮单击事件,从文本框中获取输入数据,并将数据发送回 DOM。现在我们将构建一个最小的图像编辑器,它将使用所有这些功能。
DOM API
但首先,要使 Go 代码与浏览器进行交互,我们需要一个 DOM API。我们有 syscall/js 库来帮助我们解决这个问题。它是一个非常简单却功能强大的 DOM API 形式,我们可以在其上构建我们的应用程序。在我们制作应用程序之前,让我们快速了解它的一些功能。
回调
为了响应 DOM 事件,我们声明了回调并用这样的事件将它们连接起来:
import“syscall/js”

// Declare callback
cb := js.NewEventCallback(js.PreventDefault, func(ev js.Value) {
// handle event
})

// Hook it up with a DOM event
js.Global().Get(“document”).
Call(“getElementById”, “myBtn”).
Call(“addEventListener”, “click”, cb)

// Call cb.Release() on your way out.
更新 DOM
要从 Go 中更新 DOM,我们可以
import“syscall/js”

js.Global().Get(“document”).
Call(“getElementById”, “myTextBox”).
Set(“value”, “hello wasm”)
您甚至可以调用 JS 函数并操作本机 JS 对象,如 FileReader 或 Canvas。查看 syscall/js 文档以获取更多详细信息。
正确的 Web 应用程序
接下来我们将构建一个小应用程序,它将获取输入的图像,然后对图像执行一些操作,如亮度,对比度,色调,饱和度,最后将输出图像发送回浏览器。每个效果都会有滑块,用户可以更改这些效果并实时查看目标图像的变化。
首先,我们需要从浏览器获取输入的图像给到我们的 Go 代码,以便可以处理它。为了有效地做到这一点,我们需要采取一些不安全的技巧,这里跳过具体细节。拥有图像后,它完全在我们的控制之下,我们可以自由地做任何事情。下面是图像加载器回调的简短片段,为简洁起见略有简化:
onImgLoadCb = js.NewCallback(func(args []js.Value) {
reader := bytes.NewReader(inBuf) // inBuf is a []uint8 slice where our image is loaded
sourceImg, _, err := image.Decode(reader)
if err != nil {
// handle error
}
// Now the sourceImg is an image.Image with which we are free to do anything!
})

js.Global().Set(“loadImage”, onImgLoadCb)
然后我们从效果滑块中获取用户值,并操纵图像。我们使用了很棒的 bild 库。下面是回调的一小部分:
import “github.com/anthonynsimon/bild/adjust”

contrastCb = js.NewEventCallback(js.PreventDefault, func(ev js.Value) {
delta := ev.Get(“target”).Get(“valueAsNumber”).Float()
res := adjust.Contrast(sourceImg, delta)
})

js.Global().Get(“document”).
Call(“getElementById”, “contrast”).
Call(“addEventListener”, “change”, contrastCb)
在此之后,我们将目标图像编码为 jpeg 并将其发送回浏览器。这是完整的应用程序:
加载图片:

改变对比:

改变色调:

太棒了,我们可以在浏览器中本地操作图像而无需编写一行 Javascript!源代码可以在这里找到。
请注意,所有这些都是在浏览器本身中完成的。这里没有 Flash 插件,Java Applet 或 Silverlight。而是使用浏览器本身支持的开箱即用的 WebAssembly。
最后的话
我的一些结束语:

由于 Go 是一种垃圾收集语言,因此整个运行时都在 wasm 二进制文件中。因此,二进制文件通常有几 MB 的大小。与 C /Rust 等其他语言相比,这仍然是一个痛点; 因为向浏览器发送 MB 级数据并不理想。但是,如果 wasm 规范本身支持 GC,那么这可能会改变。
Go 中的 Wasm 支持正式进行试验。syscall/js API 本身也在不断变化,未来可能会发生变化。如果您发现错误,请随时在我们 issues 报告问题。
与所有技术一样,WebAssembly 也不是一颗银弹。有时,简单的 JS 更快更容易编写。然而,wasm 规范本身正在开发中,并且即将推出更多功能。线程支持就是这样一个特性。

希望这篇文章展示了 WebAssembly 的一些很酷的方面,以及如何使用 Go 编写功能齐全的 Web 应用程序。如果您发现错误,请尝试一下,并提出问题。如果您需要任何帮助,请随时访问 #webassembly 频道。
原文链接
Go and WebAssembly: running Go programs in your browser
【译】Go 和 WebAssembly:在浏览器中运行 Go 程序

退出移动版