(原) Go与WebAssembly之三

原创文章,请后转载,并注明出处。

最新消息称 WebAssembly 成为 W3C 标准。WebAssembly 也正式抵达了 1.0 版本,它已获得了主流浏览器 Firefox、Chrome、Safari 和 Edge 的支持。

学习一下 go关于WebAssembly: https://github.com/golang/go/wiki/WebAssembly

这里提到了命令行运行go代码的工具,这样就不需要另外建立一个web服务器。

// install goexec: go get -u github.com/shurcooL/goexec

$ goexec ‘http.ListenAndServe(:8080, http.FileServer(http.Dir(.)))’

其它:浏览器中的操作系统 https://bellard.org/jslinux/

因为WebAssembly是新技术,所以golang也在不断改进,原来的js.NewCallback已经换为js.FuncOf。看下面的例子:


//从go中为js定义一个函数

package main
import (
	"fmt"
	"syscall/js"
)
func add(this js.Value, i []js.Value) interface{} {
	js.Global().Set("output", js.ValueOf(i[0].Int()+i[1].Int()))
	fmt.Printf("%v\n", js.ValueOf(i[0].Int()+i[1].Int()))
	return nil
}
func registerCallbacks() {
	js.Global().Set("add", js.FuncOf(add))
}
func main() {
	//c := make(chan struct{}, 0)
	println("Go WebAssembly Initialized")
	registerCallbacks()
	//<-c
	select{}
}

原来的方式类似于:

func foo( args []js.Value) {
    fmt.Println("hellow wasm")
    fmt.Println(args)
}

func main() {
    js.Global().Set("foo", js.NewCallback(foo))
    select {}
}

所以注意定义和注册时的不同:1.参数部份的修改 2.返回值及类型

原来网上一些开源的库也需要一并修改。我的go版为1.13.5,我是追新党。

上例中定义的add函数,只是在js中注册,并未调用。可以在html中调用,或者直接在控制台add(…)即可看出反馈结果。

在html源代码中并不能看到go或者说是wasm中的定义等,这或许也算有利于代码安全吧。以前为了获取一些资源,会去分析一些javascript代码,搞懂作者的调用机制,从而绕过。


示例:GO获取DOM元素,操作标签属性

在html中添加一个div,供wasm调用。

package main
import (
	"syscall/js"
)

func setDivRedColor(this js.Value, args []js.Value) interface{} {
    // 获取DOM元素, 进行设置属性,  call方法为调用js方法
    js.Global().Get("document").Call("getElementById", "div").Set("style", "width: 300px; height: 300px; background-color: red")
    // 注意, 此处设置style的时候, 是会覆盖掉html中的style设置
    return nil
}

func main() {
    js.Global().Set("setDivRedColor", js.FuncOf(setDivRedColor))
    select {}
}

关键就是这一句,当然原本就没几句

js.Global().Get(“document”).Call(“getElementById”, “div”).Set(“style”, “width: 300px; height: 300px; background-color: red”)

我猜: js.Global() 获取js所有资源 js.Global().Get(“document”) 获取到IE的内容部份 js.Global().Get(“document”).Call(“getElementById”, “div”) IE内容中,通过ID获取到DIV js.Global().Get(“document”).Call(“getElementById”, “div”).Set() 设置目标的相关属性


继续一个交互示例:

在html中

   <input type="text" id="v1" />
   <input type="text" id="v2" />

   <button onClick="add('v1', 'v2', 'result');" id="addButton">Add</button>

   <input type="text" id="result" />

在go中

func add(this js.Value, i []js.Value) interface{} {
	value1 := js.Global().Get("document").Call("getElementById", i[0].String()).Get("value").String()
	value2 := js.Global().Get("document").Call("getElementById", i[1].String()).Get("value").String()
	int1, _ := strconv.Atoi(value1)
	int2, _ := strconv.Atoi(value2)
	js.Global().Get("document").Call("getElementById", i[2].String()).Set("value", int1+int2)
	return nil
}

换个姿势给myBtn定义一个click事件

	cb := js.FuncOf(func(this js.Value,ev []js.Value) interface{} {
    	println("click...")
    	return nil
	})

	js.Global().Get("document").Call("getElementById", "myBtn").Call("addEventListener", "click", cb)

而html可以很简单添加click即可


在上例中作一些修改,html添加

Add1

加一个javascript:

function add1(t1,t2,t3) {
    var ret = subtract(document.getElementById(t1).value,document.getElementById(t2).value);
    document.getElementById(t3).value = ret;
}

意思是想在golang中执行功能,而html负责与前台有关的调用,无需理会前台各个关系。

在go中执行功能,返回执行结果。

func Subtract(this js.Value, i []js.Value) interface{} {
	int1, _ := strconv.Atoi(i[0].String())
	int2, _ := strconv.Atoi(i[1].String())
	//return strconv.Itoa(int1+int2)
	return int1+int2
}

js.Global().Set("subtract", js.FuncOf(Subtract))

之前试用 string(i[0].Int()) 或者 strconv.Itoa(i[0].Int()) 或者 i[0].Int() 都是错误的,提示panic: syscall/js: call of Value.Int on string。 必须String()后,再Atoi转换为整型进行运算。

相关文章