WebAssembly
WebAssembly

WebAssembly 實戰 – 讓 Go 與 JS 在瀏覽器上共舞

Larry Lu
Larry Lu
Nov 3 · 8 min read

如果你有在關注前端技術的話應該多少有聽過 WebAssembly(Wasm) 這東西,WebAssembly 是一種跑在瀏覽器上的低階語言,他在瀏覽器上能 以接近原生應用的效能執行,所以以前受限於效能而比較難做到的那些應用(高畫質遊戲、繪圖軟體等等),以後很有可能在網頁上就可以做到,光想到就覺得好興奮啊啊啊

雖然 WebAssembly 聽起來很厲害,但他的語法可不是普通的難寫

下圖是我用 WebAssembly 寫了個 fib 函數來求費氏數列的某一項。其他語言的 n-1 在 Wasm 裡面必須寫成 (i32.sub (get_local $n) (i32.const 1)) 才行,真的很有組合語言的 fu 呢 😂,原本兩三行就能解決的 fib 現在非得寫成好幾行不可,光想到頭就痛了

fib in Wasm
fib in Wasm

慶幸的是現在很多語言(C++/Go/Rust)都能透過 Emscripten 編譯成 WebAssembly,所以其實你不需要真的寫 WebAssembly,只需要把你寫完的 C++/Go/Rust 程式碼編譯成 Wasm 就好

如果從前端技術發展的角度來看:只要瀏覽器可以執行 Wasm,那各語言的開發者就能用他們熟悉的語言開發功能,然後再編譯成 Wasm 就能在瀏覽器上執行。也就是說

瀏覽器終於不是只能跑 JavaScript,而是可以跑任何能編譯成 Wasm 的語言 🎉🎉🎉

而這篇文章就是要實際用 Go 完成一些功能、編譯成 Wasm 在網頁上執行,並且比較 Wasm 跟原生 JS 的 效能

雖然說會用到 Go,但如果你對他不熟悉也沒關係,因為 Go 這個語言還滿好學的,稍微看一下官網的 A Tour of Go 就可以上工了~

瀏覽器支援

雖然 WebAssembly 這東西聽起來很高科技XD,但其實主流的瀏覽器都已經支援了哦~除非你的網站需要支援 IE 或是比較舊款的手機,不然基本上不會有問題

目前也已經有人用 WebAssembly 做出一些應用,像下面這個專案是把 Vim 編譯成 Wasm,並且讓他跑在瀏覽器上

從 Devtool 也可以看到他確實是用 WebAssembly 實作的哦(黃框處 application/wasm

上面說了這麼多都是在講 WebAssembly 技術的近況,接下來終於要開始寫扣了(摩拳擦掌

事前準備

等等會用到 Go 提供的 wasm_exec.js 作為 Wasm 跟 JS 之間溝通的橋樑,另外還需要啟動一個 Web Server(我是用 simplehttpserver),先把他們準備好

Hello World in Go

View on Github

學任何語言都要從 Hello World 開始學起,用 Wasm 當然也是,先用 Go 寫一個簡單的 Hello World

編譯時加上 GOOS=jsGOARCH=wasm,這樣 Go 就會編譯出一個 main.wasm

為了讓瀏覽器載入編譯完的 Wasm 檔案,再寫一個 index.html 來抓 main.wasm

之後下 simplehttpserver . 把 Web Server 跑起來就能在瀏覽器 Console 看到可愛的 Hello World,而且這個 Hello World 是用 Go 寫的哦,很神奇吧~

syscall/js

上面已經成功跑出 Go 寫的 Hello World,很多文章也都只有講到這裡。但我猜會用 WebAssembly 大部分都是為了解決效能的問題,所以來說說要怎麼用 Go 寫一些高效能的 function 給 JS 用

為了達到這個目的,Go 有提供一個 syscall/js package 在處理 Go 跟 JS 之間的型別轉換、函數綁定:譬如說把 int 對應到 Number、還有讓 JS 能夠使用 Go 寫的函數,如此一來就能把需要效能的地方用 Go 寫

下面的例子是用 Go 實作一個 add 負責把 args[0]args[1] 相加,並且把 add 放到全域給 JS 用

View on Github

因為 add function 已經被編譯成 WebAssembly 並放到全域,所以在 Console 裡面可以直接用

還是要再強調一次,這個 add是用 Go 寫的哦,超酷的~

既然用 JS 跟 Go(WebAssembly) 都可以完成同樣的功能,那他們的效能比起來怎麼樣?

我分別用 Go 跟 JS,實作了同樣的演算法(Merge Sort)來排序一百萬個整數,大家猜猜 Wasm 的效能會快多少?是二倍五倍還是甚至十倍呢?

View on Github

其實測出來竟然沒快多少XDD,原本還以為會快個三五倍 😂,但不管是在 Chrome 還是 Firefox 上測,WebAssembly 大概都只快了 10% 左右,看來 Wasm 也沒想像中那麼厲害嘛

好啦認真說,我猜可能是因為 WebAssembly 的編譯、執行技術才剛發展不久,還有不少待改進的地方;而 JS 因為歷史悠久、瀏覽器早已對他做了各種最佳化,所以才會得到這樣的結果,相信以後 WebAssembly 還會越來越快

DOM Manipulation

除了把 Go 寫的 function 給 JS 用之外,其實 Go 也能透過 syscall/js 提供的 API 來取得 JS 全域變數、操作 DOM

以下面實作的 hello 來說,我先用 document.createElement 建立一個 h1 Element,設定好內容 Hello World 後再把他塞到 document.body 裡面去

View on Github

這時在瀏覽器裡面執行 hello() 就會看到新增的 Hello World 了~

其實 WebAssembly 本身是不能操作 DOM 的,是因為 exec_wasm.js 把這些 DOM 操作封裝成 WASM function 了才能這樣用

雖然這例子證明了 Go 可以操作 DOM,代表你可以 只用 Go 就完成整個前端應用,但我自己是覺得滿彆扭的啦,好好的 document.body 非得寫成 js.Global().Get("document").Get("body") 不可。如果只是寫個小範例還可以,要我用這種方式做一個專案我應該會先氣死

不知道各位有沒有聽過著名的 Atwood 定律:任何可以用 JavaScript 寫出來的應用,最終都會用 JavaScript 來寫

Any application that can be written in JavaScript, will eventually be written in JavaScript.

但在 WebAssembly 的快速發展之下,其他語言也紛紛開始搶食瀏覽器這塊大餅,就連跟前端八竿子打不著的 Go 都能開始寫前端了,以後 JS 還能不能維持現在的霸主地位好像也很難說

總結

回歸到這篇的標題 — 「讓 Go 與 JS 在瀏覽器上共舞」,看完這幾個小例子想必大家都對 Go 跟 WebAssembly 有更多認識,應該也知道 Go 跟 JS 要怎麼在瀏覽器上合作了。怕大家忘記再重點整理一下今天的內容:

  • 主流瀏覽器都有支援 WebAssembly
  • Go 可以寫 function 給 JS 用
  • Go 編譯成的 WebAssembly 比 JS 稍快一點
  • Go 可以藉由 syscall/js 來操作 DOM,但很難用

範例都放在 Github 上,歡迎各位看倌自己 clone 下來跑跑看,對於文章或是範例扣有什麼問題的話也歡迎在下方留言跟我討論,謝謝大家

StarBugs Weekly 技術專欄

一群技術人想寫出一些好文章,所建立的技術專欄。每週二會發佈技術週刊,分享科技新知,歡迎大家訂閱 https://weekly.starbugs.dev/。

Larry Lu

Written by

Larry Lu

我是 Larry 盧承億,傳說中的 0.1 倍工程師。我熱愛技術、喜歡與人分享,專長是 Javascript 跟 Golang,平常會寫寫技術文章還有參加各種技術活動,歡迎大家來找我聊聊技術~

StarBugs Weekly 技術專欄

一群技術人想寫出一些好文章,所建立的技術專欄。每週二會發佈技術週刊,分享科技新知,歡迎大家訂閱 https://weekly.starbugs.dev/。

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade