【譯】Go和WebAssembly:在瀏覽器中運行Go程序

在過去很長一段時間裏,Javascript是Web開發人員中的通用語言。若是你想寫一個穩定成熟的 Web 應用程序,用javascript幾乎是惟一的方法。javascript

WebAssembly(也稱爲wasm)將很快改變這種狀況。使用WebAssembly能夠用任何語言編寫Web應用程序。在本文中,咱們將瞭解如何編寫Go程序並使用wasm在瀏覽器中運行它們。html

但首先,什麼是WebAssembly

webassembly.org 將其定義爲「基於堆棧的虛擬機的二進制指令格式」。這是一個很好的定義,但讓咱們將其分解爲咱們能夠輕鬆理解的內容。java

從本質上講,wasm是一種二進制格式; 就像ELF,Mach和PE同樣。惟一的區別是它適用於虛擬編譯目標,而不是實際的物理機器。爲什麼虛擬?由於不一樣於 C/C++ 二進制文件,wasm二進制文件不針對特定平臺。所以,您能夠在Linux,Windows和Mac中使用相同的二進制文件而無需進行任何更改。 所以,咱們須要另外一個「代理」,它將二進制文件中的wasm指令轉換爲特定於平臺的指令並運行它們。一般,這個「代理」是一個瀏覽器,但從理論上講,它也能夠是其餘任何東西。git

這爲咱們提供了一個通用的編譯目標,可使用咱們選擇的任何編程語言構建Web應用程序!只要咱們編譯爲wasm格式,咱們就沒必要擔憂目標平臺。就像咱們編寫一個Web應用程序同樣,可是如今咱們有了用咱們選擇的任何語言編寫它的優點。github

你好 WASM

讓咱們從一個簡單的「hello world」程序開始,可是要確保您的Go版本至少爲1.11。咱們能夠這樣寫:golang

package main

import (
	"fmt"
)

func main() {
	fmt.Println("hello wasm")
}
複製代碼

保存爲test.go。看起來像是一個普通的Go程序。如今讓咱們將它編譯爲wasm平臺程序。咱們須要設置GOOSGOARCHweb

$GOOS=js GOARCH=wasm go build -o test.wasm test.go
複製代碼

如今咱們生成了 wasm 二進制文件。但與原生系統不一樣,咱們須要在瀏覽器中運行它。爲此,還須要再作一點工做來實現這一目標:shell

  • Web服務器來運行應用
  • 一個index.html文件,其中包含加載wasm二進制文件所需的一些js代碼。
  • 還有一個js文件,它做爲瀏覽器和咱們的wasm二進制文件之間的通訊接口。

我喜歡把它想象成製做The PowerPuff Girls所須要的東西。編程

而後,BOOM,咱們有了一個WebAssembly應用程序!瀏覽器

如今Go目錄中已經包含了html和js文件,所以咱們將其複製過來。

$cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
$cp "$(go env GOROOT)/misc/wasm/wasm_exec.html" .
$# we rename the html file to index.html for convenience.
$mv wasm_exec.html index.html
$ls -l
total 8960
-rw-r--r-- 1 agniva agniva    1258 Dec  6 12:16 index.html
-rwxrwxr-x 1 agniva agniva 6721905 Sep 24 12:28 serve
-rw-rw-r-- 1 agniva agniva      76 Dec  6 12:08 test.go
-rwxrwxr-x 1 agniva agniva 2425246 Dec  6 12:09 test.wasm
-rw-r--r-- 1 agniva agniva   11905 Dec  6 12:16 wasm_exec.js
複製代碼

serve是Go二進制文件,是一個Web服務器。但幾乎任何Web服務器均可以。(譯者注:原文並無提供serve二進制文件的源代碼,相信聰明的你必定知道怎樣編寫。)

一旦運行它,並打開瀏覽器。能夠看到一個Run按鈕,點擊它,將執行咱們的應用程序。而後咱們點擊它並檢查控制檯:

真牛,咱們剛剛在Go中編寫了一個程序並在瀏覽器中運行它。

到如今爲止一切順利。但這是一個簡單的「hello world」程序。真實的Web應用程序須要與DOM交互。咱們須要響應按鈕單擊事件,從文本框中獲取輸入數據,並將數據發送回DOM。如今咱們將構建一個最小的圖像編輯器,它將使用全部這些功能。

DOM API

但首先,要使Go代碼與瀏覽器進行交互,咱們須要一個DOM API。咱們有syscall/js庫來幫助咱們解決這個問題。它是一個很是簡單卻功能強大的DOM API形式,咱們能夠在其上構建咱們的應用程序。在咱們製做應用程序以前,讓咱們快速瞭解它的一些功能。

回調

爲了響應DOM事件,咱們聲明瞭回調並用這樣的事件將它們鏈接起來:

import "syscall/js"

// Declare callback
cb := js.NewEventCallback(js.PreventDefault, func(ev js.Value) {
	// handle event
})


// Hook it up with a DOM event
js.Global().Get("document").
	Call("getElementById", "myBtn").
	Call("addEventListener", "click", cb)


// Call cb.Release() on your way out.
複製代碼

更新DOM

要從Go中更新DOM,咱們能夠

import "syscall/js"

js.Global().Get("document").
		Call("getElementById", "myTextBox").
		Set("value", "hello wasm")
複製代碼

您甚至能夠調用JS函數並操做本機JS對象,如 FileReaderCanvas。查看syscall/js文檔以獲取更多詳細信息。

正確的 Web 應用程序

接下來咱們將構建一個小應用程序,它將獲取輸入的圖像,而後對圖像執行一些操做,如亮度,對比度,色調,飽和度,最後將輸出圖像發送回瀏覽器。 每一個效果都會有滑塊,用戶能夠更改這些效果並實時查看目標圖像的變化。

首先,咱們須要從瀏覽器獲取輸入的圖像給到咱們的Go代碼,以即可以處理它。爲了有效地作到這一點,咱們須要採起一些不安全的技巧,這裏跳過具體細節。擁有圖像後,它徹底在咱們的控制之下,咱們能夠自由地作任何事情。下面是圖像加載器回調的簡短片斷,爲簡潔起見略有簡化:

onImgLoadCb = js.NewCallback(func(args []js.Value) {
	reader := bytes.NewReader(inBuf) // inBuf is a []uint8 slice where our image is loaded
	sourceImg, _, err := image.Decode(reader)
	if err != nil {
		// handle error
	}
	// Now the sourceImg is an image.Image with which we are free to do anything!
})

js.Global().Set("loadImage", onImgLoadCb)
複製代碼

而後咱們從效果滑塊中獲取用戶值,並操縱圖像。咱們使用了很棒的bild庫。下面是回調的一小部分:

import "github.com/anthonynsimon/bild/adjust"

contrastCb = js.NewEventCallback(js.PreventDefault, func(ev js.Value) {
	delta := ev.Get("target").Get("valueAsNumber").Float()
	res := adjust.Contrast(sourceImg, delta)
})

js.Global().Get("document").
		Call("getElementById", "contrast").
		Call("addEventListener", "change", contrastCb)
複製代碼

在此以後,咱們將目標圖像編碼爲jpeg並將其發送回瀏覽器。這是完整的應用程序:

加載圖片:

改變對比:

改變色調:

太棒了,咱們能夠在瀏覽器中本地操做圖像而無需編寫一行Javascript! 源代碼能夠在這裏找到。

請注意,全部這些都是在瀏覽器自己中完成的。這裏沒有Flash插件,Java Applet或Silverlight。而是使用瀏覽器自己支持的開箱即用的WebAssembly。

最後的話

個人一些結束語:

  • 因爲Go是一種垃圾收集語言,所以整個運行時都在wasm二進制文件中。所以,二進制文件一般有幾MB的大小。與C/Rust等其餘語言相比,這仍然是一個痛點; 由於向瀏覽器發送MB級數據並不理想。可是,若是wasm規範自己支持GC,那麼這可能會改變。

  • Go中的Wasm支持正式進行試驗。syscall/js API自己也在不斷變化,將來可能會發生變化。若是您發現錯誤,請隨時在咱們issues報告問題。

  • 與全部技術同樣,WebAssembly也不是一顆銀彈。有時,簡單的JS更快更容易編寫。然而,wasm規範自己正在開發中,而且即將推出更多功能。線程支持就是這樣一個特性。

但願這篇文章展現了WebAssembly的一些很酷的方面,以及如何使用Go編寫功能齊全的Web應用程序。若是您發現錯誤,請嘗試一下,並提出問題。若是您須要任何幫助,請隨時訪問 #webassembly頻道。

原文連接

Go and WebAssembly: running Go programs in your browser

【譯】Go和WebAssembly:在瀏覽器中運行Go程序

相關文章
相關標籤/搜索