You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.3 KiB
5.3 KiB
Go & ViteJS
Go
-
Go, linguaggio di programmazione compilato, statically typed e con una libreria standard con tutte le cose essenziali.
-
Ideale per scrivere server HTTP.
ViteJS
- On demand file serving over native ESM, no bundling required!
- Hot Module Replacement (HMR) that stays fast regardless of app size.
- Out-of-the-box support for TypeScript, JSX, CSS and more.
- Pre-configured Rollup build with multi-page and library mode support.
- Rollup-superset plugin interface shared between dev and build.
- Flexible programmatic APIs with full TypeScript typing.
Documentazione di ViteJS
Creazione progetto in Golang
Inizializziamo il progetto in Go per la backend
# inizializziamo il file go.mod
$ go mod init gdg-talk-counter
# o anche così...
$ go mod init github.com/aziis98/gdg-talk-counter
Creazione progetto in ViteJS
Ed il progetto in ViteJS per la frontend
$ npm init # creates the "package.json" file
$ npm install -D vite
(o anche con un altro package manager alternativo a npm, ad esempio io userò pnpm
)
Il file "package.json"
{
...
"scripts": {
"dev": "vite", // avvia il server di ViteJS in modalità di development
"build": "vite build" // crea la cartella "dist/" con tutti gli asset e bundle
},
...
}
$ npm run dev
$ npm run build
Il file "src/main.go"
Impostiamo un piccolo server sulla porta :4000
per servire gli asset e l'API
package main
...
func main() {
mux := http.NewServeMux()
setupRoutes(mux) // vedremo meglio questo in seguito
server := http.Server{
Addr: ":4000",
Handler: mux,
}
log.Fatal(server.ListenAndServe())
}
Configurazione di ViteJS
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:4000/',
},
},
})
ViteJS come proxy per il server in Go
Development:
~~~graph-easy --as=boxart
[ The developer ] -- :3000 --> [ Dev. Server ]
~~~
~~~graph-easy --as=boxart
[ The developer ] -- :3000/api/... --> [ Dev. Server ] -- :4000/api/... --> [ Backend ]
~~~
Production:
~~~graph-easy --as=boxart
[ The user ] -- :4000 --> [ Backend ]
~~~
Il file "index.html"
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GDG Talk Counter</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>GDG Talk Counter</h1>
<div class="counter">Carico...</div>
<button id="btn-increment">Incrementa</button>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Inizializzazione della frontend (VanillaJS)
function mountApp($root) {
const $counter = $root.querySelector('.counter')
const $btnIncrement = $root.querySelector('#btn-increment')
const Counter = mountCounter($counter)
$btnIncrement.addEventListener('click', async () => {
Counter.increment()
})
setInterval(async () => {
Counter.refresh()
}, 1000)
}
mountApp(document.body)
Codice per il contatore
import { fetchJSON } from './utils.js'
function mountCounter($counter) {
function render(count) {
$counter.textContent = `Contatore: ${count}`
}
async function refresh() {
const count = await fetchJSON('/api/counter')
render(count)
}
async function increment() {
const count = await fetchJSON('/api/increment', { method: 'POST' })
render(count)
}
refresh()
return { refresh, increment }
}
Tornando alla backend
func setupRoutes(mux *http.ServeMux) {
// The database
counter := 0
// GET /api/status
mux.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
...
})
// GET /api/counter
mux.HandleFunc("/api/counter", func(w http.ResponseWriter, r *http.Request) {
...
})
// POST /api/increment
mux.HandleFunc("/api/increment", func(w http.ResponseWriter, r *http.Request) {
...
})
// Static Files
mux.Handle("/", http.FileServer((http.Dir("./dist/"))))
}
La route GET /api/status
type object map[string]any
mux.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(&object{ "status": "ok" })
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
mux.HandleFunc("/api/value", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(counter)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})