Co je WebAssembly?
Než přejdeme k psaní kódu, pojďme nejprve pochopit, co je WebAssembly. WebAssembly nebo WASM je jazyk podobný assembleru, který lze v prohlížeči spustit s téměř nativním výkonem. Nemá být napsán ručně, ale má být považován za cíl kompilace pro jazyky jako C/C++, Golang, Rust, .Net atd. To znamená, že nejprve napíšeme program v jazyce, poté jej převedeme na WASM a poté spustit v prohlížeči. To umožní programu běžet téměř nativní rychlostí a umožní spustit program napsaný v libovolném jazyce, který lze spustit v prohlížeči. Webové aplikace můžete vytvářet v jazyce, který znáte. Neznamená to, že odstraní javascript, ale existuje ruku v ruce s JavaScriptem. Seznam jazyků, které podporují kompilaci WASM, je v úžasné-wasm-langs a další informace na webové stránce WebAssembly a WebAssembly Concepts.
Spusťte v prohlížeči
Teď si ušpiníme ruce nějakým základním WASM a Golangem.
Psaní kódu Go
Pojďme napsat náš první program hello world.
package main
import "fmt"
func main() {
fmt.Println("Hi from the browser console!!")
}
Kompilace do WebAssembly
Pojďme to zkompilovat do Wasm.
GOOS=js GOARCH=wasm go build -o main.wasm main.go
Tím se vytvoří main.wasm
Soubor WebAssembly, který můžeme importovat a spustit v prohlížeči.
Integrace s javascriptem
Poté, co napíšeme náš Go kód a zkompilujeme jej do WASM, můžeme jej začít integrovat do prohlížeče.
K interakci s Go through wasm budeme potřebovat runtime wrapper Go napsaný v javascriptu. Kód je dodáván s Go 1.11+ a lze jej zkopírovat pomocí následujícího příkazu:
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
Nyní jej integrujeme do prohlížeče.
<html>
<head>
<meta charset="utf-8" />
<script src="wasm_exec.js"></script>
<script>
const go = new Go()
WebAssembly.instantiateStreaming(
fetch('main.wasm'),
go.importObject
).then(result => {
go.run(result.instance)
})
</script>
</head>
<body></body>
</html>
WebAssembly.instantiateStreaming
zkompiluje a vytvoří instanci kódu WebAssembly. Po vytvoření instance kódu spustíme program Go s go.run(result.instance)
. Další informace najdete v dokumentech WebAssembly.instantiateStreaming a Go WebAssembly.
Nyní, když spustíme server pro poskytování obsahu, můžeme zobrazit výstup v konzole prohlížeče.
Můžeme použít goexec
pro poskytování souborů:
# Install go exec
go get -u github.com/shurcooL/goexec
# Start the server at 8080 port
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
Pokud otevřeme localhost:8080
v prohlížeči a otevřete konzoli, uvidíme naši zprávu odeslanou z Go:
Přístup k webovým rozhraním API a zpřístupnění funkcí Go
Nyní, když víme, jak zkompilovat a spustit kód Go to Wasm a spustit jej na webu, začněme s vytvářením převodníku Image to Ascii v prohlížeči pomocí Web APIs
. WebAssembly může spolupracovat s různými webovými rozhraními API, jako je DOM
, CSSOM
, WebGL
, IndexedDB
, Web Audio API
atd. V tomto tutoriálu budeme používat DOM
Rozhraní API v kódu Go s pomocí syscall/js
balíček poskytovaný v Golangu.
package main
import (
"syscall/js"
)
func main() {
c := make(chan bool)
//1. Adding an <h1> element in the HTML document
document := js.Global().Get("document")
p := document.Call("createElement", "h1")
p.Set("innerHTML", "Hello from Golang!")
document.Get("body").Call("appendChild", p)
//2. Exposing go functions/values in javascript variables.
js.Global().Set("goVar", "I am a variable set from Go")
js.Global().Set("sayHello", js.FuncOf(sayHello))
//3. This channel will prevent the go program to exit
<-c
}
func sayHello(this js.Value, inputs []js.Value) interface{} {
firstArg := inputs[0].String()
return "Hi " + firstArg + " from Go!"
}
Výše uvedený kód ukazuje, jak můžeme plně interagovat s rozhraním API prohlížeče pomocí experimentálního balíčku Go syscall/js
. Proberme výše uvedený příklad.
js.Global()
metoda se používá k získání globálního objektu Javascript, který je window
nebo global
. Poté můžeme přistupovat ke globálním objektům nebo proměnným jako document
, window
a další javascriptová API. Pokud chceme získat nějakou vlastnost z javascriptového prvku, použijeme obj.Get("property")
a nastavit vlastnost obj.Set("property", jsDataType)
. Můžeme také volat funkci javascriptu pomocí Call
metodu a předejte argumenty jako obj.Call("functionName", arg1,arg1)
. Ve výše uvedeném příkladu jsme přistoupili k objektu dokumentu, vytvořili značku h1 a přidali ji do těla HTML pomocí DOM API.
Ve druhé části kódu jsme odhalili funkci Go a nastavili proměnnou, ke které lze přistupovat pomocí javascriptu. goVar
je proměnná typu string a sayHello
je typ funkce. Můžeme otevřít naši konzoli a pracovat s exponovanými proměnnými. Definice funkce pro sayHello
lze vidět v poslední části kódu, která přebírá argument a vrací řetězec.
Na konci hlavního bloku čekáme na kanál, který nikdy nepřijme zprávu. To se provádí, abychom udrželi kód Go spuštěný, abychom měli přístup k exponované funkci. V jiných jazycích, jako je C++ a Rust Wasm s nimi zachází jako s knihovnou, tj. můžeme je přímo importovat a začít používat exponované funkce. V Go se však s importem zachází jako s aplikací, tj. můžete přistupovat k programu, když se spustí a běží, a poté je interakce ukončena, když je program ukončen. Pokud nepřidáme kanál na konec bloku, nebudeme schopni zavolat funkci, která byla definována v Go.
Výše uvedený kód vytváří následující výstup:
Import obrázku do knihovny Ascii do prohlížeče
Nyní, když víme, jak komunikovat tam a zpět mezi Go a prohlížečem, pojďme vytvořit aplikaci v reálném světě. Budeme importovat existující knihovnu image2Ascii, která převádí obrázek na znaky ASCII. Jedná se o aplikaci Go CLI, která vezme cestu obrázku a převede jej na znaky Ascii. Protože nemůžeme přistupovat k systému souborů v prohlížeči přímo, upravil jsem některé kódy v knihovně tak, aby místo cesty k souboru vzaly bajty obrázku. Zdroj do repozitáře se změnami:wasm-go-image-to-ascii. Musíme se starat pouze o odhalené API z knihovny, spíše než o to, jak algoritmus zatím funguje. Odhaluje následující:
func ImageFile2ASCIIString(imgByte []byte, option *Options) string
type Options struct {
Colored bool `json:"colored"`
FixedWidth int `json:"fixedWidth"`
FixedHeight int `json:"fixedHeight"`
Reversed bool `json:"reversed"`
}
Rozdělme celý proces na následující úkoly:
- Vytvořte posluchač událostí pro vstup souboru, který předá vybraný obrázek naší funkci Go.
- Napište funkci Go pro převod obrázku do ASCII a jeho vystavení prohlížeči.
- Vytváření a integrace do prohlížeče.
Vytvořte posluchač událostí pro vstup souboru
Budeme pokračovat za předpokladu funkce s názvem convert(image, options)
bude vytvořen Go.
document.querySelector('#file').addEventListener(
'change',
function() {
const reader = new FileReader()
reader.onload = function() {
// Converting the image to Unit8Array
const arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer)
// Call wasm exported function
const txt = convert(
array,
JSON.stringify({
fixedWidth: 100,
colored: true,
fixedHeight: 40,
})
)
// To convert Ansi characters to html
const ansi_up = new AnsiUp()
const html = ansi_up.ansi_to_html(txt)
// Showing the ascii image in the browser
const cdiv = document.getElementById('console')
cdiv.innerHTML = html
}
reader.readAsArrayBuffer(this.files[0])
},
false
)
Přidali jsme change
posluchače na vstup s id file
. Jakmile uživatel obrázek vybere, odešleme jej převedením na Unit8Array
na convert
funkce.
Funkce Go pro převod obrázku do ASCII
package main
import (
"encoding/json"
_ "image/jpeg"
_ "image/png"
"syscall/js"
"github.com/subeshb1/wasm-go-image-to-ascii/convert"
)
func converter(this js.Value, inputs []js.Value) interface{} {
imageArr := inputs[0]
options := inputs[1].String()
inBuf := make([]uint8, imageArr.Get("byteLength").Int())
js.CopyBytesToGo(inBuf, imageArr)
convertOptions := convert.Options{}
err := json.Unmarshal([]byte(options), &convertOptions)
if err != nil {
convertOptions = convert.DefaultOptions
}
converter := convert.NewImageConverter()
return converter.ImageFile2ASCIIString(inBuf, &convertOptions)
}
func main() {
c := make(chan bool)
js.Global().Set("convert", js.FuncOf(converter))
<-c
}
Vystavujeme convert
funkce, která bere bajty obrazu a možnosti. Používáme js.CopyBytesToGo
převést javascript Uint8Array
přejít na []byte
. Po převedení obrázku funkce vrátí řetězec znaků Ascii/Ansi.
Sestavení a integrace do prohlížeče
Nakonec můžeme sestavit kód wasm a importovat jej do prohlížeče.
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/BrowserFS/2.0.0/browserfs.js"></script>
<script src="https://cdn.jsdelivr.net/gh/drudru/ansi_up/ansi_up.js"></script>
<script src="wasm_exec.js"></script>
</head>
<body>
<!-- ASCII Image container -->
<pre
id="console"
style="background: black; color: white; overflow: scroll;"
></pre>
<!-- Input to select file -->
<input type="file" name="file" id="file" />
<script>
// Integrating WebAssembly
const go = new Go()
WebAssembly.instantiateStreaming(
fetch('main.wasm'),
go.importObject
).then(result => {
go.run(result.instance)
})
// Adding image change listener
document.querySelector('#file').addEventListener(
'change',
function() {
const reader = new FileReader()
reader.onload = function() {
// Converting the image to Unit8Array
const arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer)
// Call wasm exported function
const txt = convert(
array,
JSON.stringify({
fixedWidth: 100,
colored: true,
fixedHeight: 40,
})
)
// To convert Ansi characters to html
const ansi_up = new AnsiUp()
const html = ansi_up.ansi_to_html(txt)
// Showing the ascii image in the browser
const cdiv = document.getElementById('console')
cdiv.innerHTML = html
}
reader.readAsArrayBuffer(this.files[0])
},
false
)
</script>
</body>
</html>
Zde je odkaz na úložiště:https://github.com/subeshb1/wasm-go-image-to-ascii
Závěr
Podívali jsme se na základy Wasm a na to, jak jej používat k importu kódu Go do prohlížeče. Podívali jsme se také na to, jak můžeme importovat existující knihovnu a vytvořit aplikaci v reálném světě pro převod obrázků na znaky ASCII. Podělte se o své myšlenky a zpětnou vazbu v sekci komentářů a sdílejte svůj projekt také ve WebAssembly. Přestože je Wasm v rané fázi, vidíme, jak užitečné může být odstranění jazykové závislosti na prohlížeči a zlepšení výkonu spuštěním téměř nativní rychlosti.
- Základní příklad uvedený v blogu:https://github.com/subeshb1/Webassembly/tree/master/go
- Wasm image do ASCII:https://github.com/subeshb1/wasm-go-image-to-ascii
- Ukázka:https://subeshbhandari.com/app/wasm/image-to-ascii
Další zdroje na WebAssembly:
- Awesome Wasm:https://github.com/mbasso/awesome-wasm
- WebAssembly z MDN:https://developer.mozilla.org/en-US/docs/WebAssembly