Go的WebAssembly初见

Go的WebAssembly初见
Go WebAssembly

介 绍

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

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

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

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

WebAssembly 简介

WebAssembly[1] 是一种新兴的网页虚拟机标准,它的设计目标包括高可移植性、高安全性、高效率(包括载入效率和运行效率)、尽可能小的程序体积,WebAssembly 程序在 JavaScript 环境下的使用方法、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
Go的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 并对网页进行刷新。

Go的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. 你也可以通过其他方式启动一个服务器来浏览。

效 果👍

Go的WebAssembly初见
运行效果

相关函数解析

  • 通过 js.Global().Get("btn")document.Call("getElementById", "hello")两种方式获取到 DOM 元素。
  • btnEle 调用 addEventListener 为 btn 绑定点击事件 sayHello
  • helloEle 调用 Set("innerHTML", ...) 渲染计算结果。
  • JavaScript代码中WebAssembly.instantiateStreaming(....)是加载我们go代码编译的wasm文件,然后网页就具备我们刚才使用go写的功能了。

生命周期

Go的WebAssembly初见
WebAssembly生命周期

在go里面调用其他语言的wasm

通过上面的例子可以看出来,我们写的go代码可以编译成wasm扔给浏览器执行我们的逻辑,反之我们也可以通过go来操作其他语言编译的wasm文件,并且调用里面的函数。

  1. 首先我们需要安装一个依赖库
  2. 然后我们开启cgo
  3. 然后把我们准备一个tests.wasm文件
# Enable cgo 启动cgo
export CGO_ENABLED=1; export CC=gcc;
# 拉取我们需要的解析库
github.com/wasmerio/go-ext-wasm

注意这里有一个坑,官方那个github主页是让你去下载go get github.com/wasmerio/wasmer-go,其实目前不是这个了,我感觉是那个老外故意阻挡我们gopher去学习wasm的步伐,想让我们直接入坑rust,啊哈哈哈,开个玩笑拉,✅正确方式去下载这个包github.com/wasmerio/go-ext-wasm,不过在你们的看到这个文章的时候我已经发起pr请求准备修复这个坑,啊哈哈

项目官方地址https://github.com/wasmerio/wasmer-go

Go的WebAssembly初见
操作步骤截图
  1. 然后在我们的main.go文件中写入
package main

import (
    "fmt"

    wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)

func main() {
    // 将WebAssembly模块读取为字节
    bytes, _ := wasm.ReadBytes("tests.wasm")

    // 实例化WebAssembly模块
    instance, _ := wasm.NewInstance(bytes)
    defer instance.Close()

    // 从WebAssembly实例获取`sum`导出的函数。
    sum := instance.Exports["sum"]

    // 用Go标准值调用导出的函数。WebAssembly
    // 推断类型,并自动转换值
    result, _ := sum(1111)

    // 结果
    fmt.Println(result) // 22!
}

  1. 执行
go run main.go 
22

小 结

从上面的例子我们可以看出来,wasm应用案例不止于此,例如很多第三方的发送短信的API接口SDK都是不同语言版本的代码实现,如果换成wasm,直接写一套代码编译成wasm,提供一个函数,其他语言加载这个wasm文件就可以了,只需要一个wasm解决了…..

相关资料

  • 本例子代码仓库http://github.com/higker/hello-wasm-go
  • https://golang.org/pkg/syscall/js
  • https://github.com/golang/go/wiki/WebAssembly

Javascript的API文档

  • https://developer.mozilla.org/zh-cn/docs/web/javascript/reference/global_objects/webassembly/instantiatestreaming

技术交流群

Go的WebAssembly初见
微信交流群
Go的WebAssembly初见
公众号

参考资料

[1]

WebAssembly: https://webassembly.org/


原文始发于微信公众号(TPaper):Go的WebAssembly初见

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/23756.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!