使用 VUE 和 Go 触摸 WebAssembly

简介: 笔记

6.png

本文将展示如何在 Go 中使用 WebAssembly。本文一起来学习如何从 Go 代码构建到 WebAssembly,通过VUE来展示使用 WebAssembly 的API。

本文涉及的 Go 需要 Go1.11 或更高版本的 Go 开发环境,这里将忽略 Go 环境的配置。前端将使用 VUE2 来构建。

文章涉及代码:github.com/QuintionTan…

7.png


什么是 WebAssembly?


WebAssembly(wasm)是指一种可以在浏览器及其外围技术和工具中运行的编程语言。它以二进制格式表示并由堆栈机器实现处理。与 JavaScript 一样,它由浏览器直接解释,但正在开发和规范,目标是在速度方面超越 JavaScript。

WebAssembly 大多由 C、C++、Rust 等各种高级语言编译而成,而不是程序员直接编写二进制代码。同样在 Go 中,将 Go 代码编译为 WebAssembly 的功能从 Go1.11正式添加为 Go 的标准功能。

更多内容如下:


HelloWorld


Go 有一个叫做交叉编译的特性。不仅可以为正在编译的机器的体系结构和操作系统构建二进制文件,还可以为其他体系结构和操作系统构建二进制文件。

例如,在 macOS 上为 Windows 和 Linux 交叉编译二进制文件非常容易。可以通过指定以下环境 GOOS 变量来像往常一样进行交叉编译:GOARCH go bulid


# 为 Windows 编译(32 位)
$ GOOS=windows GOARCH=386 go build
# 为 Linux 编译(64 位)
$ GOOS=linux GOARCH=amd64 go build

创建目录 go-webassembly,进入目录,再创建 helloworld ,进入 helloworld 目录,执行命令:


go mod init go-webassembly/helloworld

创建文件 main.go ,代码如下:


package main
func main() {
  println("Hello, WebAssembly!")
}

前端实现将使用 VUE 框架来展示其调用效果,因此需要创建文件夹 vue ,将把 WebAssembly 生成的 wasmjs 文件存储到项目目录 public/wasms

现在以交叉方式编译 WebAssembly,在目录下并执行如下命令。请注意,此处将输出文件名指定为选项,但即使不指定也可以构建。GOOS js GOARCH wasm go build-o

GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/helloworld/main.wasm

执行完命令后,将在目录下生成文件 main.wasm,同时将在 GOROOT 目录下生成 wasm_exec.js 文件,完整路径为 /usr/local/go/misc/wasm,将文件复制到路径 /vue/public/wasms/helloworld/ 下,命令如下:

cp /usr/local/go/misc/wasm/wasm_exec.js .
cp /usr/local/go/misc/wasm/wasm_exec_node.js .
cp /usr/local/go/misc/wasm/wasm_exec.html .

这样目录 /vue/public/wasms/helloworld 就有两个文件 jswasm,接下来执行命令:

node wasm_exec_node.js main.wasm

输出的结果如下:

Hello, WebAssembly!

接下来就是在 VUE 中来展示 Helloworld 的调用,在 public/index.html 中引入JS,如下:

<script src="./wasms/helloworld/wasm_exec.js"></script>

构建组件 Helloworld,完整代码如下:

<template>
    <div class="card">
        <div class="card-header">
            <h4>Helloworld</h4>
        </div>
        <div class="card-body">
            <p class="text-muted">
                点击“运行”,在控制台输出日志 <code>Hello, WebAssembly!</code>
            </p>
            <div class="live-preview">
                <button
                    @click="run()"
                    class="btn btn-success"
                    id="runButton"
                    disabled
                >
                    运行
                </button>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: "Helloworld",
    data() {
        return {
            go: null,
            mod: null,
            inst: null,
        };
    },
    mounted() {
        this.init();
    },
    methods: {
        init() {
            if (!WebAssembly.instantiateStreaming) {
                WebAssembly.instantiateStreaming = async (
                    resp,
                    importObject
                ) => {
                    const source = await (await resp).arrayBuffer();
                    return await WebAssembly.instantiate(source, importObject);
                };
            }
            const go = new window.Go();
            this.go = go;
            WebAssembly.instantiateStreaming(
                fetch("/wasms/helloworld/main.wasm"),
                go.importObject
            )
                .then((result) => {
                    console.log(result);
                    this.mod = result.module;
                    this.inst = result.instance;
                    document.getElementById("runButton").disabled = false;
                })
                .catch((err) => {
                    console.error(err);
                });
        },
        async run() {
            console.clear();
            await this.go.run(this.inst);
            this.inst = await WebAssembly.instantiate(
                this.mod,
                this.go.importObject
            );
        },
    },
};

</script>

点击按钮“运行”,在浏览器控制台输入如下:

8.png

处理 JavaScript 对象


接下来,学习如何在 JavaScript 中使用对象,为了更好的处理 Go 中的 JavaScript 对象,将使用 Go1.11 标准包中包含的包 syscall/js

syscall/js 里面定义了个新的类型 js.Value,它表示一个JavaScript值,它提供了一个简单的API来操纵任何类型的JavaScript值并与之交互。一个 js.ValueOf() 函数,它接受任何 Go 基本类型并返回相应的 js.Value

Go值和 JavaScript 值对应关系如下:

Go JavaScript
js.Value JavaScript 中的任何值
js.Func function
nil null
bool Boolean
integers 和 floats Number
string String
[]interface{} new array
map[string]interface{} new object

JavaScript 中的 js.Type 类型表示为类型。js.Type 类型定义如下,可以从 js.Value 类型的方法中检索。


type Type int
const (
  TypeUndefined Type = iota
  TypeNull
  TypeBoolean
  TypeNumber
  TypeString
  TypeSymbol
  TypeObject
  TypeFunction
)

js.Value 类型将所有 JavaScript 值表示为单一类型,因此如果每个方法都调用了一个意外的值,panic 就会导致崩溃。例如,Int 方法可以 js.Value 将类型的值视为数字并将 int 值作为 Go 类型检索。但是,js.Value 类型也可以处理函数和字符串值,所以当调用一个不是数字的值时,panic 会发生错误。

因此,js.Type 通过使用 type 值,js.Value可以处理 type 值的具体类型,避免 panic。例如,对于Int 方法,最好只在 Type 方法返回 js.TypeNumber 时调用。如下:


func printNumber(v js.Value) {
        if v.Type() == js.TypeNumber {
                fmt.Printf("%d\n", v.Int())
        }
}


DOM 操作


可以在 Go 中使用 js.Value 来更好的操作 HTML Dom 对象。接下来创建目录 docments ,创建文件 main.go ,代码如下:


package main
import "syscall/js"
func main() {
  // 获取全局对象(网页浏览器为window)
  window := js.Global()
  // window.document.getElementById("helloresult")
  message := window.Get("document").Call("getElementById", "helloresult")
  // HTML
  message.Set("innerHTML", "Hello, WebAssembly")
}

接下来在前端创建一个 id="helloresult" 的 DOM 对象。

按照上面的流程,生成 wasm 文件:

GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/document/main.wasm

事件处理


上面介绍了如何操作 DOM,现在来实现时间的处理。

package main
import "syscall/js"
func main() {
        window := js.Global()
        // window.document.getElementById("clickresult") 
        message := window.Get("document").Call("getElementById", "clickresult")
    cb := js.FuncOf(func(this js.Value, args []js.Value) any { 
        message.Set("innerHTML", "Go 事件触发")
        return nil
    })
        // message.addEventListener("click", cb)
        message.Call("addEventListener", "click", cb)
        select {}
}

按照上面的流程,生成 wasm 文件:

GOOS=js GOARCH=wasm go build -o ../vue/public/wasms/events/main.wasm


总结


本文介绍了如何将 Go 代码构建为 WebAssembly、如果实现 Go 与 JavaScript 对象及回调函数。


相关文章
|
6月前
|
JavaScript
vue : 无法加载文件 D:\module\npm_module\npm_modules\vue.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.c
vue : 无法加载文件 D:\module\npm_module\npm_modules\vue.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.c
|
JSON JavaScript 前端开发
如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块? 下
如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块?
216 0
|
JSON 编解码 Rust
如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块? 上
如何通过 JavaScript 运行用 Go 编写的 WebAssembly 模块?
181 0
|
Web App开发 Go JavaScript
用 go 写 WebAssembly入门
Golang WebAssembly 入门 Golang 在1.11版本中引入了 WebAssembly 支持,意味着以后可以用 go编写可以在浏览器中运行的程序,当然这个肯定也是要受浏览器沙盒环境约束的.
2199 0
|
16天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
26 7
|
15天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
16天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
92 71
|
15天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
100 67
|
18天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
88 62
|
16天前
|
存储 Go
go语言中映射
go语言中映射
32 11