By Weerasak Chongnguluam

ลองเล่น Go-App สร้าง Progressive Web App ด้วย Go


เมื่อวานไปเจอบทความว่า Dragger เปลี่ยน Web Frontend จาก React Typescript ไปเป็น Go (https://dagger.io/blog/replaced-react-with-go) และบอกว่าใช้ package go-app ช่วยในการเขียน frontend ด้วย Go โดย compile ไปเป็น WebAssembly

เลยลองดูบ้างว่าเป็นยังไง วิธีสร้างก็ไม่ยากเริ่มง่าย ๆ เช่นสร้าง project ใหม่เราก็สร้าง go module ใหม่กันก่อนเลย

mkdir hello-go-app
cd hello-go-app
go mod init hello-go-app

จากนั้นเราก็ go get dependency กันก่อน

go get github.com/maxence-charriere/go-app/v10/pkg/app

จากนั้นสร้างไฟล์ main.go แล้วเขียนโค้ดกันแบบนี้เพื่อแสดง Hello, World! บน browser

package main

import (
	"log"
	"net/http"

	"github.com/maxence-charriere/go-app/v10/pkg/app"
)

type hello struct {
	app.Compo
}

func (h *hello) Render() app.UI {
	return app.H1().Text("Hello, World!")
}

func main() {
	app.Route("/", func() app.Composer {
		return &hello{}
	})

	app.RunWhenOnBrowser()

	http.Handle("/", &app.Handler{
		Name:        "Hello",
		Description: "An Hello World example",
	})

	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}
  • เราสร้าง struct type ใหม่ชื่อ hello แล้วให้มี embedded field app.Compo เข้ามา
  • จากนั้น implements method Render() app.UI เพื่อ return UI ที่เราต้องการแสดงสำหรับ component นี้
  • ใน main() เราก็สร้าง route ใหม่แล้วให้ component hello ที่เราสร้างแสดงเมื่อเข้า path /
  • เรียก app.RunWhenOnBrowser() เพื่อให้เราสามารถเรียกใช้งานบน browser ได้
  • สร้าง http server แล้วให้ handler ของเราเป็น app.Handler แล้ว listen ที่ port 8000

จากนั้นเราจะ compile ส่วนที่จะทำงานบน browser ก่อนเป็น WebAssembly ด้วยคำสั่ง

GOARCH=wasm GOOS=js go build -o web/app.wasm

ซึ่งต้องกำหนด output ไว้ที่ web/app.wasm เพราะว่า go-app จะไปหาไฟล์นี้เมื่อเราเรียกใช้งานบน browser

แล้วก็ต้อง compile อีกรอบสำหรับส่วนที่เป็น http server ด้วยคำสั่ง

go build -o server

จากนั้นเราก็สามารถ run server ได้เลย

./server

เท่านี้ก็จะได้ server ที่รันอยู่ที่ http://localhost:8000 แล้วเราก็เข้าไปดูผลลัพธ์บน browser ได้

hello-go-app

มาดูอีกตัวอย่างลองสร้าง Counter ที่มีปุ่ม เพิ่ม ลบ แล้วให้แสดงค่า counter ที่เพิ่มขึ้นหรือลดลง

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/maxence-charriere/go-app/v10/pkg/app"
)

type counter struct {
	app.Compo
	count int
}

func (c *counter) Render() app.UI {
	return app.Main().Body(
		app.H1().Text("Counter"),
		app.P().Text(fmt.Sprintf("Count: %d", c.count)),
		app.Button().Name("increment").Text("Increment").OnClick(
			func(ctx app.Context, e app.Event) {
				c.count++
			},
		),
		app.Button().Name("decrement").Text("Decrement").OnClick(
			func(ctx app.Context, e app.Event) {
				c.count--
			},
		),
	)
}

func main() {
	app.Route("/", func() app.Composer {
		return &counter{}
	})

	app.RunWhenOnBrowser()

	http.Handle("/", &app.Handler{
		Name:        "Counter",
		Description: "An Counter example",
	})

	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

เราสามารถเพิ่ม field ให้กับ type struct ของ component ของเราเพื่อเก็บ state ของ component ได้เลย แล้ว event handler ของ button เราก็ทำการเปลี่ยนค่าของ field นี้ได้เลย แล้ว component จะ rerender ให้เอง

counter-app