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
272 lines
5.3 KiB
Markdown
2 years ago
|
# 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
|
||
|
}
|
||
|
})
|
||
|
```
|