1. 程式人生 > >不安分的 Go 語言開始入侵 Web 前端領域了!( WebAssembly )

不安分的 Go 語言開始入侵 Web 前端領域了!( WebAssembly )

fat 開發 zip input com pack 引擎 通過 就會

參考:https://blog.csdn.net/csdnnews/article/details/84038848

從 Go 語言誕生以來,它就開始不斷侵蝕 Java 、C、C++ 語言的領地。今年下半年 Go 語言發布了 1.11 版本,引入了 WebAssembly 技術,瀏覽器端 Javascript 的壟斷地位也開始遭遇 Go 語言的攻擊。這次不同以往,它意味著 Go 語言從後端滲透進了前端,進入了一個全新的世界。

WebAssembly 是一項比較新的技術,只有比較現代的瀏覽器才支持 WebAssembly,例如 Chrome、FireFox瀏覽器。

Go 編譯器可以將代碼編譯成 WebAssembly 二進制字節碼,被瀏覽器以靜態資源的形式加載進來後轉換成 Javascript 模塊。有了這個模塊,瀏覽器可以直接操縱 Go 語言生成的二進制字節碼邏輯。同時在 Go 語言編寫的代碼中可以直接讀寫瀏覽器裏面 Javascript 運行時對象,這樣就完成了 Javascript 和 Go 代碼的雙向交互。

Go 語言直到 1.11 版本之後才開啟了對 WebAssembly 的支持。如需體驗,必須升級。

第一步

使用 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 二進制字節碼。(我的是windows)

set GOARCH=wasmset 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">
    <meta http-equiv="expires" content="0"> 
    <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))
}

現在在項目目錄下有四個文件:main.go、index.html、fib.wasm、wasm_exec.js

用以下命令編譯運行:

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 來完成項目依然是最佳選擇。

原作者簡介:老錢,著有《Redis 深度歷險》《深入理解 RPC》《快學 Go 語言》。

熟練使用 Java、Python、Golang 等多種計算機語言,開發過遊戲,制作過網站,寫過消息推送系統和 MySQL 中間件,實現過開源的 ORM 框架、Web 框架、RPC 框架等,目前任職掌閱服務端技術專家。

不安分的 Go 語言開始入侵 Web 前端領域了!( WebAssembly )