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.

272 lines
5.3 KiB
Markdown

# 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
```bash
# 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
```bash
$ 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"
```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
},
...
}
```
```bash
$ 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
```go
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
```js
// 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"
```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)
```js
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
```js
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
```go
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`
```go
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
}
})
```
```go
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
}
})
```