Gopher 的 WebAssembly 实践

Posted WebAssembly 中文社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gopher 的 WebAssembly 实践相关的知识,希望对你有一定的参考价值。

Go WebAssembly

本文内容来自微信公众号 一手代码一首诗 !

原标题《Go 的 WebAssembly 初见》

介 绍

在 go 1.11 的时候 go 官方向 go 实验性的添加了 WebAssembly 支持,也就是说 golang 可以编译 为 wasm 以供 javascript 进行调用。

在这里我想说一下我的感受,在编写代码时应该是主要编写 go 的代码供 JavaScript 调用,而不是在 go 中调用 JavaScript 代码。

目前 go 还只是实验性的支持 wasm 以后可能会变更,后面我会尽量跟进。

另外如果感兴趣的话,推荐一本柴树杉、丁尔男两位大佬的《WebAssembly 标准入门》,估计也快发售了。

WebAssembly 简介

WebAssembly 是一种新兴的网页虚拟机标准,它的设计目标包括高可移植性、高安全性、高效率(包括载入效率和运行效率)、尽可能小的程序体积。本书详尽介绍了 WebAssembly 程序在 JavaScript 环境下的使用方法、WebAssembly 汇编语言和二进制格式,给出了大量简单易懂的示例,同时以 C/C++/Rust/Go 语言开发环境为例,介绍了如何使用其他高级语言开发 WebAssembly 模块。另外WebAssembly有好几个干爹:万维网联盟、 Mozilla、微软、谷歌、 苹果。

Hello Wasm

由于目前只是实验性的支持,在编写代码时 引用 syscall/js 会爆红 需要在goland中将变量环境 指定为 OS=js ARCH=wasm goland 的设置在 seting -> Go -> Build Tags && Vendoring 中进行更改。

我使用的是 chrome 浏览器 , 包括 js 部分演示我也是在 chrome 下进行。

  1. 创建一个项目 hello-wasm-go
Gopher 的 WebAssembly 实践
hello-wasm-go
  1. 创建 main.go 并写入以下内容
package main

import "fmt"

func main() {
    fmt.Println("Hello, WebAssembly!")
}
  1. 然后将 main.go 编译为 main.wasm
GOOS=js GOARCH=wasm go build -o main.wasm
  1. 创建 index.html 并写入以下内容
<html>
<head>
    <meta charset="utf-8"/>
    <script src="wasm_exec.js"></script>
    <script>
    const go = new Go();
    WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
        go.run(result.instance);
    });
    </script>
</head>
<body></body>
</html>
  1. 将 JavaScript 支持文件移动到本目录
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

下载 go 版的 简单 web 服务器

go get -v -u github.com/shurcooL/goexec

然后运行它

goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'

然后打开 chrome 访问 http://127.0.0.1:8080/ 并按下 F12 打开 DevTools 不出意外的话你应该会看到 控制台上输出 Hello, WebAssembly!

接下来就正式开始了,在每次更改 go 代码时 你需要对 go 代码进行重新编译,重启 goexec 并对网页进行刷新。

Gopher 的 WebAssembly 实践
GoWebAssembly

在js里面调用go的wasm

接下来我们玩一点不一样的,go 调用 JavaScript 函数并且回显到前端页面,也就是在前端调用我们使用go写的代码逻辑。

  1. 首先我们创建一个 main.goindex.html
// Copyright (c) 2020 HigKer
// Open Source: MIT License
// Author: SDing <deen.job@qq.com>
// Date: 2021/1/7 - 4:26 下午 - UTC/GMT+08:00

package main

import (
    "fmt"
    "syscall/js"
)

// 通过Go代码操作页面dom节点
var (
    document = js.Global().Get("document")
    nameEle  = document.Call("getElementById""name")
    helloEle = document.Call("getElementById""hello")
    btnEle   = js.Global().Get("btn")
)

func sayHello(this js.Value, args []js.Value) interface{} {
    name := nameEle.Get("value").String()
    if len(name) == 0 {
        name = "github.com/higker/hello-wasm-go"
    }
    str := fmt.Sprintf("Hello,%s", name)
    helloEle.Set("innerHTML", js.ValueOf(str))
    return nil
}
func main() {
    done := make(chan int0)
    btnEle.Call("addEventListener""click", js.FuncOf(sayHello))
    <-done
}

下面是index.html内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello WebAssembly For Golang</title>
</head>
<body style="text-align: center">
    <p>You Name:</p>
    <input id="name" type="text">
    <button id="btn">Running</button>
    <h1 id="hello"></h1>
</body>
<script src="wasm_exec.js"></script>
<script>
    const go = new Go();
    WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
        go.run(result.instance);
    });
</script>
</html>
  1. 编译代码 GOOS=js GOARCH=wasm go build -o main.wasm

  2. 复制依赖文件cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

  3. 启动静态服务器

goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
  1. 你也可以通过其他方式启动一个服务器来浏览。

效 果

以上是关于Gopher 的 WebAssembly 实践的主要内容,如果未能解决你的问题,请参考以下文章

视频WebAssembly 在白鹭引擎中的实践

你想要的WebAssembly入门与实践

在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?

记一次完整 C++ 项目编译成 WebAssembly 的实践

认识 WebAssembly 与 Rust 实践

Blazor WebAssembly - 用户帐户和身份服务器的最佳实践