不安分的 Go 语言开始入侵 Web 前端领域了
WebAssembly 这个名字翻译过来就是 「Web 汇编」,也就是 Web 端的汇编语言。它是一段二进制字节码程序,Javascript 可以将这段二进制程序编译成模块,然后再实例化这个模块就可以调用字节码逻辑了。WebAssembly 代码运行的速度很快,比 Javascript 要快很多,Javascript 可以通过 WebAssembly 技术将关键性耗费性能的逻辑交给 WebAssembly 来做就可以明显提升浏览器端的性能。
对比显示,使用 WebAssembly 运行斐波那契数列相比使用原生 Javascript 来实现,运行效率上能带来 3.5 倍的提升。
WebAssembly 是一项比较新的技术,只有比较现代的浏览器才支持 WebAssembly,例如 Chrome、FireFox浏览器。
图片 Go WebAssembly 运行原理Go 编译器可以将代码编译成 WebAssembly 二进制字节码,被浏览器以静态资源的形式加载进来后转换成 Javascript 模块。有了这个模块,浏览器可以直接操纵 Go 语言生成的二进制字节码逻辑。同时在 Go 语言编写的代码中可以直接读写浏览器里面 Javascript 运行时对象,这样就完成了 Javascript 和 Go 代码的双向交互。
Go 语言直到 1.11 版本之后才开启了对 WebAssembly 的支持。如需体验,必须升级。
Go WebAssembly 初体验
下面我们就开始体验一下 Chrome 浏览器与 Go 代码是如何交互的。我们要实现一个功能,在浏览器的输入框里输入一个正整数,然后调用 Go 代码的斐波那契数列,再将结果再呈现在页面上。涉及到 4 个文件,分别是 fib.go、main.go、index.html、wasm_exec.js。
第一步
使用 Go 代码编写 WebAssembly 模块文件 fib.go,将 Go 语言实现的斐波那契函数注册到 Javascript 全局环境。这需要使用内置的 syscall/js 模块,它提供了和 Javascript 引擎交互的接口。
// fib.go
package main
import "syscall/js"
func main() {
f_fib := func(params []js.Value) {
var n = params[0].Int() // 输入参数
var callback = params[1] // 回调参数
var result = fib(n)
// 调用回调函数,传入计算结果
callback.Invoke(result)
}
// 注册全局函数
js.Global().Set("fib", js.NewCallback(f_fib))
// 保持 main 函数持续运行
select {}
}
// 计算斐波那契数
func fib(n int) int {
if n <= 0 {
return 0
}
var result = make([]int, n+1)
result[0] = 0
result[1] = 1
if n <= 1 {
return result[n]
}
for i:=2;i<=n;i++ {
result[i] = result[i-2] + result[i-1]
}
return result[n]
}
Go 语言注册到 Javascript 引擎的函数在执行时是异步的,所以这个函数没有返回值,在完成计算后需要通过调用「传进来的回调函数」将结果传递到 Javascript 引擎。注意 main 函数要保持运行状态不要退出,不然注册进去的 fib 函数体就销毁了。
第二步
下面将 Go 代码编译成 WebAssembly 二进制字节码。
$ GOARCH=wasm GOOS=js go build -o fib.wasm fib.go
执行完成后可以看到目录下多了一个 fib.wasm,这个就是字节码文件。它的大小是 1.3M,作为静态文件传递到浏览器似乎有点大,不过静态文件服务器一般有 gzip 压缩,压缩后的大小只有几百K,这差不多也可以接受了。
第三步
编写网页文件 index.html,这个网页包含两个输入框,第一个输入框用来输入整数参数,第二个输入框用来呈现计算结果。当第一个输入框内容发生改变时,调用 javascript 代码,执行通过 WebAssembly 注册的 fib 函数。需要传入参数 n 和回调的函数。
<html>
<head>
<meta charset="utf-8">
<title>Go wasm</title>
</head>
<style>
body {
text-align: center
}
input {
height: 50px;
font-size: 20px;
}
#result {
margin-left: 20px;
}
</style>
<body>
<script src="wasm_exec.js"></script>
<script>
// 容纳 WebAssembly 模块的容器
var go = new Go();
// 下载 WebAssembly 模块并执行模块
// 也就是运行 Go 代码里面的 main 函数
// 这样 fib 函数就注册进了 Javascript 全局环境
WebAssembly.instantiateStreaming(fetch("fib.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
function callFib() {
let paramInput = document.getElementById("param")
let n = parseInt(paramInput.value || "0")
// 传入输入参数和回调函数
// 回调函数负责呈现结果
fib(n, function(result) {
var resultDom = document.getElementById("result")
resultDom.value = result
})
}
</script>
// 输入发生变化时,调用 WebAssembly 的 fib 函数
<input type="number" id="param" oninput="callFib()"/>
<input type="text" id="result" />
</body>
</html>
注意代码中引入了一个特殊的 js 文件 wasm_exec.js,这个文件可以从 Go 安装目录的 misc 子目录里找到,将它直接拷贝过来。它实现了和 WebAssembly 模块交互的功能。
第四步
运行静态文件服务器,这里不能使用普通的静态文件服务器,因为浏览器要求请求到的 WebAssemly 字节码文件的 Content-Type 必须是 application/wasm,很多静态文件服务器并不会因为扩展名是 wasm 就会自动使用这个 Content-Type。但是 Go 内置的 HTTP 服务器可以。所以下面我们使用 Go 代码简单编写一个静态文件服务器。
package main
import (
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.Handle("/", http.FileServer(http.Dir(".")))
log.Fatal(http.ListenAndServe(":8000", mux))
}
使用下面的命令运行它
$ go run main.go
第五步
打开浏览器,访问 http://localhost:8000,现在就可以体验它的运行效果了。
Javascript 真的需要担心 Go WebAssembly 的威胁么?其实根本不用担心,WebAssembly 的目的是替换前端运行比较耗时的逻辑,不是用来替换前端框架的,它也替换不了。虽然开源社区冒出了一个 https://github.com/elliotforbes/oak 的 Go WebAssembly 框架,可以让你使用 Go 语言编写前端应用程序。但是我仔细看了一下它的的源码,发现它原来只是一个玩具 ^_^,实现上没几行代码,离真实的应用程序差距太远。
如果 Go WebAssembly 对 javascript 是个威胁,那么威胁 javascript 的可不止 Go 语言了,能够将代码编译成 WebAssembly 字节码的语言多达几十种。
希望将当前 javascript 项目的部分代码替换成 Go 语言,成本也是显而易见的。技术栈的切换成本,字节码的加载成本,框架项目持续集成的成本都是需要考虑的点。除非能获得巨大的性能提升,否则使用纯粹的 javascript 来完成项目依然是最佳选择。
原文发布时间为: 2018-11-06
本文作者:码洞
本文来自云栖社区合作伙伴“码洞”,了解相关信息可以关注“码洞”。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
浅谈网络爬虫中深度优先算法和简单代码实现
学过网站设计的小伙伴们都知道网站通常都是分层进行设计的最上层的是顶级域名之后是子域名子域名下又有子域名等等同时每个子域名可能还会拥有多个同级域名而且URL之间可能还有相互链接千姿百态由此构成一个复杂的网络。 当一个网站的URL非常多的时候我们务必要设计好URL否则在后期的理解、维护或者开发过程中就会非常的混乱。理解以上的网页结构设计之后现在正式的引入网络爬虫中的深度优先算法。 上图是一个二叉树结构通过对这个二叉树的遍历来类比抓取网页加深对爬虫策略的理解。深度优先算法的主要思想是首先从顶级域名A开始之后从中提取出两个链接B和C待链接B抓取完成之后下一个要抓取的链接则是D或者E而不是说抓取完成链接B之后立马去抓取链接C。抓取完链接D之后发现链接D中所有的URL已经被访问过了在这之前我们已经建立了一个被访问过的URL列表专门用于存储被访问过的URL。当链接D完全被抓取完成之后接下来就会去抓取链接E。待链接E爬取完成之后不会去爬取链接C而是会继续往下深入的去爬取链接I。原则就是链接会一步一步的往下爬只要链接下还有子链接且该子链接尚未被访问过这就是深度优先算法的主要思想。深度优先算法是让爬虫一步...
- 下一篇
IDEA 2018.3 EAP,我只看好你这四个功能!
作为 IDEA 的死忠粉,这篇文章早就该写了,一直拖到现在,虽然有点晚,还是想跟大家分享一下。 相信很多人一旦与 IDEA 结缘,便会慢慢地喜欢上它,就如同我跟它的故事一样。如果你对 IDEA 还不熟悉,可以读读我之前写的这两篇文章来帮你快速入门。 谈谈我与 Intellij IDEA 的故事 Intellij IDEA 中我一直在用的几个插件 关于这次的更新,官方推文很早都提到了,目前还未正式发布。 总的来说,我挺看好这四个功能,分别是插件中心、Run Anything、JVM Debugger、JVM Profiler。 很多人说这次提供了一个高亮的主题,说实话我不是太喜欢,还是喜欢以前的 Darcula。 对于版本控制模块的更新,我没有感觉到特别新奇,因为我习惯用命令行。 对于编辑器的改进,我感觉对我的影响不是太大,因为我觉得掌握了 ctrl+e 就很 6 了,可以查看我之前的文章。 这次的搜索功能也进行了加强,总的来说,是对以前搜索功能进行部分补充与完善。 插件中心 无论是一款工具,还是一个开源框架,它们的成功都离不开一个良好的插件中心,IDEA 的成功也不例外,都在于整个社区...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果