hic003cih / Golang

0 stars 0 forks source link

一段帶著 Vue 從 Koa 迫降到 Gin 小故事 #42

Open hic003cih opened 4 years ago

hic003cih commented 4 years ago

愛的迫降:一段帶著 Vue 從 Koa 迫降到 Gin 小故事 傳說中 Vue 與 Gin 的邂逅 Leon Lin (林為志) Leon Lin (林為志) Follow Mar 12 · 9 min read

在開發網頁的時候,除了撰寫前端時選用當前討論度最高的三大框架(Angular、React、Vue) 外,後端通常也會選擇 Express 或是 Koa 的 Node.js 框架來進行開發,由於同樣使用 JavaScript,因此大幅降低了熟悉新語言的投資門檻;使用 Node.js 開發後端也有很長一段時間了,因此有點心癢想換個新的方式來做做新的專案,近年來隨著 Golang 的熱度提高,於是乎開始研究了一下由 Golang 開發的一套 Web 框架 Gin 。 實作目標 我們將使用 Vue 與 Vuetify 來撰寫一頁登陸網頁 (Landing Page),並使用 Gin 的框架來啟動 Server;不過本文會比較著重在描述 Gin 的開發。

前情提要 在開始動手之前,我們需要下列開發工具: Vue 開發環境 (Vue.js、Vuetify、Webpack、npm、Babel ...) Node.js (如果需要管理 Node.js 版本的話可另外安裝 nvm) gvm (Golang 的版本安裝工具,本文將以 go1.12.15 進行) Gin (本文主角) 開始著手第一支 API 裝好 go1.12.15 之後,先建立一個空的 Go 專案: $ mkdir ginserver $ cd ginserver $ go mod init ginserver init 之後,在資料夾裡會產生一個 go.mod 檔,接下來我們就可以安裝 Gin 囉!安裝的方式如下: $ go get -u github.com/gin-gonic/gin

安裝完成之後你會看到所有相依的工具也一併安裝進來了,接著我們在同一個目錄底下新增一個 main.go 作為專案的入口,依循官網範例的 Quickstart 啟動 API Server: package main

import "github.com/gin-gonic/gin"

func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run(":3002") // listen and serve on 0.0.0.0:8080 } 我更換成了自己設定的 port 3002 (如果沒設定的話會跑在預設的 8080 port),儲存之後即可編譯執行檔: $ go build

這時候會編譯出一個名為 ginserver 的執行檔;執行的方式很簡單,你可以選擇使用 go run 或是直接執行程式即可: $ go run ginserver or $ ./ginserver

接著打開瀏覽器輸入網址 http://localhost:3002/ping,看到下面的畫面就表示我們的第一個 API 完成囉!

使用 Vue 撰寫登陸頁面 由於 Vue 是在 JavaScript 環境開發的,因此我們要使用 npm 在同資料夾中初始化一個專案: $ npm init 由於我之前已有使用 Koa.js 寫好的網頁框架,所以這邊我直接將 Vue 相關的元件移到本案來使用,撰寫頁面的過程暫略。 使用 Gin 來啟動 Vue 網頁 載入 HTML 檔案 把 Vue 網頁的相關檔案移過來後,我們開始使用 Gin 來啟動 Server;一開始我們先將 HTML 檔案載入,而我的 HTML 檔案在根目錄的 templates 資料夾中:

package main import ( "github.com/gin-gonic/gin" "log" ) func main() { log.Println("Initializing ApiServer module...") router := gin.Default() router.LoadHTMLGlob("templates/") router.Run(":3002") } 載入檔案的方式有兩種,分別是 LoadHTMLGlob() 和 LoadHTMLFiles(),由於我們要使用路由,所以選用了LoadHTMLGlob() 這個方式來告知 Gin 要去哪裡拿檔案。 設定網頁路由 package main import ( "github.com/gin-gonic/gin" "log" "net/http" ) func main() { log.Println("Initializing ApiServer module...") router := gin.Default() router.LoadHTMLGlob("templates/") router.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Hello Gin", }) }) router.Run(":3002") } 範例為設定網址的根路徑直接載入 index.html 檔渲染,因為我們有使用常數判斷如 http.StatusOK,所以要多載入一個 Go 模組 net/http,確保 API 狀態正常的時候才會載入 HTML 檔;另外我們也多帶了一組標題給前端 (“title”: “Hello Gin”),使得未來網頁的標題可由 Server 端做客製化設定,前端取得資料的方式如下: <!DOCTYPE html>

{{ .title }}

設定靜態檔案路由 因為我們載入 JS 檔案的時候使用的是相對路徑,因此在 Server 端需要設定靜態檔案的取得路徑: package main import ( "github.com/gin-gonic/gin" "log" "net/http" ) func main() { log.Println("Initializing ApiServer module...") router := gin.Default() router.LoadHTMLGlob("templates/") router.Static("/public", "public") router.Static("/assets", "public/assets") router.GET("/", func(c gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Hello Gin", }) }) router.Run(":3002") } 範例為我將 webpack 編譯好的檔案放在 public/assets 裡面,因此設定如上 啟動 Server 編譯完 Go 之後我們嘗試啟動 Server 試試:

如果看到以上畫面就表示你成功囉! 最後附上專案資料夾的根目錄

使用 Vue 前端路由的問題 在 vue-router 新增一個路徑為 /contact: routes: [ { path: '/', name: 'LandingPage', component: LandingPage }, { path: '/contact', name: 'Contact', component: Contact }, { path: "*", component: PageNotFound } ] 新增完後我們重新啟動 Server ,會發現連結直接過去的時候運作正常,但是頁面重整的時候會發生 page not found 的問題:

這是因為前端的路由與後端的路由對不上,因此後端渲染的時候會找不到檔案,這時候解決的方式有兩種,第一種是根據前端的路由一個一個同步在後端如: router.GET("/", func(c gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Hello Gin", }) }) router.GET("/contact", func(c gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Hello Gin", }) }) 但是這樣的方式會提高維護難度,因此建議大家使用第二種方式解決: router.NoRoute(func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Hello Gin", }) }) 這樣的話路由就會全部交由前端處理了。