
ลองเล่น 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 ได้
มาดูอีกตัวอย่างลองสร้าง 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 ให้เอง