在以前的第一篇文章:juejin.im/post/5e9ee0… 中,大概介绍了怎么使用Go去编写代码,而后将代码编译成wasm,以后能够在js中使用wasm。是一个很是简单的demo,这篇文章主要会用来说解两个方面的知识html
原本不想介绍,由于网上已经有不少这方面的介绍了,可是为了你们在看这系列文章的时候,能够有个快速的了解,这里仍是大概介绍下wasm。前端
特色:node
这里简单说一下为何wasm执行速度快?首先,wasm更加接近机器语言,咱们知道,在计算机中运行最快的就是机器语言,由于机器能够直接运行,不须要编译,转换等各类操做。wasm呢,它是一种二进制格式的文件,体积很是小,至少比js要小很是多,这样在加载的时候,首先就比js快了对不对?其次,wasm它还能够一边接收服务端的返回数据,一边就开始解码,这你应该明白对不对?js都是要下载完整个js以后才能开始解释执行。数组
基础类型:浏览器
wasm里面只有这4种基础类型,因此你若是须要处理字符串,那就须要转换为int类型的数组,而后wasm才能处理。安全
函数支持:bash
跟其余语言的函数同样,没什么好说的。模块化
几个阶段:函数
另外还有一点很重要,wasm它没有本身特定的开发语言,须要你是用别的语言,好比说Go,Rust。这样,你用Go或者Rust写好你的代码以后呢,将代码编译成wasm,这样在js中就能使用了。post
因此,若是你正好会Go或者Rust的话,是否是优点就来了?由于至少目前来看,前端使用wasm的场景仍是很是多的。
首先介绍下js中一些关于wasm的API。具体详细的API能够参考: developer.mozilla.org/zh-CN/docs/…
这个对象里面,包括了全部可使用的WebAssembly的功能。若是你的浏览器没有这个对象,只能说明你用的多是远古时期的浏览器。
对吧,你看这个浏览器兼容性就知道了,出了很是坑的IE(谁如今还要支持IE的,举个手?),其余浏览器都是支持的。
compile方法用来将wasm的二进制代码到一个WebAssembly.Module 对象。这个应该很容易理解对吧?由于返回过来的是二进制呀,你若是须要在js中使用的话,你必需要转换为js中能够操做的东西,对吧。这个方法就是干这个事情的。
WebAssembly.Module就是经过compile()方法编译以后,获得输出,可是这个输出若是要使用呢,如今仍是不行的,还须要实例化,这个跟class有点相似了。
这个方法用来实例化WebAssembly代码,注意,这里有两种方式使用这个方法。第一种就是,你直接给wasm的二进制数据传给这个方法,还有一种就是你能够调用compile方法先编译一下,而后再去调用instantiate来实例化。
具体例子看这里: developer.mozilla.org/zh-CN/docs/… 已经很是清楚了。
重点来了,若是你阅读过上篇文章,应该见过这个方法。来,上图:
这个方法直接接收一个fetch的Response对象,直接从这里来初始化,这就比上面的方法方便多了。
到这里,js中加载wasm就完成了,下面看下怎么在js中使用wasm中导出的方法。
上面文章中,好像不太明显,看不出来,从新来看个例子:
看下上面截图中的代码,会发现,Go写出来的wasm,只能使用Go给你的那个js库来运行。可是,在Go里面,直接给js环境注入方法,这种就跟导出有点相似了,不过这里有一点很差的是,可能会致使全局变量污染,你懂的。
看下代码:
js.Global().Set("test", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fmt.Println("test invoked")
return nil
}))
signal := make(chan int)
<- signal
复制代码
改为以下代码以后,将index.html里面的js代码修改下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="test">test</button>
</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);
// 调用test方法
test();
});
</script>
</html>
复制代码
不一样的地方就是多了一个在go.run以后,调用了test函数,这个函数是go给我猛注入的。
看下执行结果:
到这里你应该会发现,若是这样直接注入全局变量或者函数的话,对应用来讲,仍是不太友好的,毕竟有可能会冲突,会形成全局变量污染。这就有点坑了。那有什么优雅点的方式呢?
仍是有的,首先,咱们修改下index.html里面的js内容:
<script>
// 用来提供给Go挂着导出的函数用的对象,要定义全局变量才行
var target = {}
const go = new Go()
WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject)
.then(result => {
go.run(result.instance);
// 调用test方法
target.test();
});
</script>
复制代码
其余内容就不贴了,跟上面的同样。这里有一个不一样的点是,在第一行,定一个一个target的全局对象,而后在调用test的时候,使用了target.test()来调用。
可是你发现,target上根本没有test方法啊,怎么调用的。这就须要看下Go的代码了:
js.Global().Get("target").Set("test", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
fmt.Println("test invoked")
return nil
}))
signal := make(chan int)
<- signal
复制代码
在Go的代码中,首先从全局对象中获取target对象,而后给这个对象设置一个test的函数。这样就能够在js中使用了。
相对于上面的直接给全局对象上设置函数以外,这种方式相对来讲优雅一些,由于可以挂载的对象,咱们能够事先定义好,对吧,这样在Go里面,或者js里面,都知道这个是用来挂载Go中导出的函数的,不像上面,你是没有办法知道,我要怎么访问Go里面导出的函数的,由于不知道去哪里找,去全局对象上找?那你怎么知道这个是否是GO挂载上去的呢,对吧。
要想在wasm中访问js中的方法,其实很简单了,看上面的代码应该就能猜到了,Go里面既然能够往js的对象上挂载函数,那确定也能直接访问js对象上的函数。
把index.html里面的js代码修改下:
<script>
var target = {
test() {
console.log('test method in js');
}
}
const go = new Go()
WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject)
.then(result => {
go.run(result.instance);
// 调用test方法
// target.test();
});
</script>
复制代码
将Go代码修改下:
js.Global().Get("target").Get("test").Invoke();
signal := make(chan int)
<- signal
复制代码
看下结果:
这篇文章主要介绍了wasm,js中访问wasm中导出的对象,wasm中访问js中的对象。这里由于使用的是Go来做为wasm的开发语言,因此可能和别的语言有点不一样。特别是Go里面是直接给js的环境挂载对象,而不是像wasm规范中说的,提供exports对象,对吧。可是到这里为止,咱们发现,js和wasm二者已经能够实现互相通讯了,那对于开发来讲,就没有任何阻碍了,能够很方便的使用Go来开发wasm,而后在js中使用起来。
有任何不对的地方,欢迎指正,谢谢!🙏