🦜
GoとSvelteKitの組み合わせ
あらかじめ必要なもの
- npm(6.14以降)
- Go(1.16以降)
プロジェクトの開始
go mod init sample
npm init svelte@next frontend
// Choice "Svelte app template" is "Skelton Project".
// Choice "Use TypeScript" is No.
// Choice "ESLint" is No.
// Choice "Prettier" is No.
cd frontend
npm install
npm i -D @sveltejs/adapter-static@next
開発モードとリリースモード
development.go
// +build !release
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func init() {
u, err := url.Parse("http://localhost:3000/")
if err != nil {
log.Fatal(err)
}
http.Handle("/", httputil.NewSingleHostReverseProxy(u))
}
release.go
// +build release
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
//go:generate sh -c "cd frontend; npm run build"
//go:embed frontend/build/*
var content embed.FS
func init() {
pub, err := fs.Sub(content, "frontend/build")
if err != nil {
log.Fatal(err)
}
http.Handle("/", http.FileServer(http.FS(pub)))
}
main.go
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
)
func logger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Print(r.RequestURI)
h.ServeHTTP(w, r)
})
}
func health(w http.ResponseWriter, r *http.Request) {}
func main() {
port := 8080
flag.IntVar(&port, "p", 8080, "http listen port")
flag.Parse()
log.SetFlags(log.Lshortfile)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatal(err)
}
defer l.Close()
log.Println("listen:", l.Addr())
http.HandleFunc("/api/health", health)
server := &http.Server{Handler: logger(http.DefaultServeMux)}
go func() {
if err := server.Serve(l); err != nil {
log.Fatal(err)
}
}()
<-ctx.Done()
server.Shutdown(ctx)
}
以下の様にsvelteの構成ファイルにstatic-adapterを追加します。
frontend/svelte.config.js
import adapter from "@sveltejs/adapter-static";
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
fallback: "index.html",
}),
},
};
export default config;
SvelteKitの新しいバージョンでは開発やビルドにviteコマンドを使うようになりました。
npm install --global vite
をして以下の設定ファイルを追加しましょう。
frontend/vite.config.js
import { sveltekit } from "@sveltejs/kit/vite";
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
};
開発モード
バックエンドサーバーを起動します。
go run . -p 8080
別のターミナルにてnpm開発サーバーを起動します(localhost:3000)。
cd frontend; npm run dev
http://localhost:8080/ を開きます。
バックエンドサーバーのAPI以外のアクセスはlocalhost:3000に中継します。
この場合、npmの開発サーバーのホットリロード機能が機能します。
リリースモード
go generate -tags release ./...
go build -tags release -o build/sample .
以上の操作でSvelteKitの静的ファイル生成アダプターによる生成物をバイナリに埋め込んだ実行ファイルが出力されます。
所感
- Svelteのコンポーネントの作成と利用ができる
- 2つのモードのビルドは双方ともに爆速なので開発体験が良い
- 全てのリソースはバイナリにバンドルされるためデプロイが非常にシンプル
- フロントエンドの開発体験を損なうことなく開発できる
- SvelteKitはレイアウトテンプレートやルーティングの機能もあるため、それなりにリッチなSPAでも構築できる
- APIを除くリソースは静的コンテンツに落とし込まれるのでCDNとの相性が良い
- Svelteの1コンポーネント1ESモジュールという作りは快適かつデバッグしやすい
Discussion