使用Golang的Gin框架和vue編寫web應用

  • 2019 年 10 月 4 日
  • 筆記

擼了今年阿里、頭條和美團的面試,我有一個重要發現…….>>>

背景: 之前使用Golang的Gin框架進行一些運維內部後端的API介面開發,對外提供提供json類型的數據響應,但是該種方式在瀏覽器訪問數據時數據格式不友好(由於是API介面,一般需要使用postman之類的工具來驗證介面返回數據),後來嘗試了使用Golang的template模板來結合html進行數據渲染,但也發現比較缺乏美感。之後決定使用前端框架來渲染後端數據,由於vue框架的各種優勢,比如簡單、數據的雙向綁定等等好處,決定使用vue框架來開啟我的前端之旅。接下來簡單來講解下使用Golang後端和vue前端進行融合的示例。

基於Gin框架的後端API

編寫基於Gin框架的API:

# 查看源碼文件  $ cat main.go  /**   * @File Name: main.go   * @Author: xxbandy @http://xxbandy.github.io   * @Email:   * @Create Date: 2018-12-02 22:12:59   * @Last Modified: 2018-12-02 22:12:52   * @Description:   */  package main    import (  	_ "fmt"  	"github.com/gin-gonic/gin"  	"math/rand"  	"net/http"  )    func HelloPage(c *gin.Context) {  	c.JSON(http.StatusOK, gin.H{  		"message": "welcome to bgops,please visit https://xxbandy.github.io!",  	})  }    func main() {  	r := gin.Default()  	v1 := r.Group("/v1")  	{  		v1.GET("/hello", HelloPage)  		v1.GET("/hello/:name", func(c *gin.Context) {  			name := c.Param("name")  			c.String(http.StatusOK, "Hello %s", name)  		})    		v1.GET("/line", func(c *gin.Context) {  			// 注意:在前後端分離過程中,需要注意跨域問題,因此需要設置請求頭  			c.Writer.Header().Set("Access-Control-Allow-Origin", "*")  			legendData := []string{"周一", "周二", "周三", "周四", "周五", "周六", "周日"}  			xAxisData := []int{120, 240, rand.Intn(500), rand.Intn(500), 150, 230, 180}  			c.JSON(200, gin.H{  				"legend_data": legendData,  				"xAxis_data":  xAxisData,  			})  		})  	}  	//定義默認路由  	r.NoRoute(func(c *gin.Context) {  		c.JSON(http.StatusNotFound, gin.H{  			"status": 404,  			"error":  "404, page not exists!",  		})  	})  	r.Run(":8000")  }    # 運行程式  $ go run main.go  [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.    [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.   - using env:	export GIN_MODE=release   - using code:	gin.SetMode(gin.ReleaseMode)    [GIN-debug] GET    /v1/hello                 --> main.HelloPage (3 handlers)  [GIN-debug] GET    /v1/hello/:name           --> main.main.func1 (3 handlers)  [GIN-debug] GET    /v1/line                  --> main.main.func2 (3 handlers)    # 測試相關的介面  $  curl -s  localhost:8000/v1/hello | python -m json.tool  {      "message": "welcome to bgops,please visit https://xxbandy.github.io!"  }    $ curl -s  localhost:8000/v1/hello/bgops  Hello bgops    $ curl -s  localhost:8000/v1/line  {"legend_data":["周一","周二","周三","周四","周五","周六","周日"],"xAxis_data":[120,240,81,387,150,230,180]}    # 可以看到該介面會返回一個json結構的數據  $ curl -s  localhost:8000/v1/line | python -m json.tool  {      "legend_data": [          "u5468u4e00",          "u5468u4e8c",          "u5468u4e09",          "u5468u56db",          "u5468u4e94",          "u5468u516d",          "u5468u65e5"      ],      "xAxis_data": [          120,          240,          347,          59,          150,          230,          180      ]  }

基於vue框架的前端項目

使用vue-cli腳手架快速構建一個vue項目。 注意:前提是需要node環境,並且有可用的npm源

# 查看版本  $ npm -v  2.3.0    #升級 npm  cnpm install npm -g    # 升級或安裝 cnpm  npm install cnpm -g    # 最新穩定版  $ cnpm install vue    # 全局安裝 vue-cli  $ cnpm install --global vue-cli  # 創建一個基於 webpack 模板的新項目  ➜  vue-doc vue init webpack vue-test    ? Target directory exists. Continue? Yes  ? Project name vue-test  ? Project description A Vue.js project  ? Author xxbandy  ? Vue build standalone  ? Install vue-router? Yes  ? Use ESLint to lint your code? Yes  ? Pick an ESLint preset Standard  ? Set up unit tests Yes  ? Pick a test runner jest  ? Setup e2e tests with Nightwatch? Yes  //提供了兩種方式[npm和yarn,如果默認選擇npm時會去外網下載資源,可能無法訪問Google外網]  ? Should we run `npm install` for you after the project has been created? (recommended) no       vue-cli · Generated "vue-test".    # Project initialization finished!  # ========================    To get started:      cd vue-test    npm install (or if using yarn: yarn)    npm run lint -- --fix (or for yarn: yarn run lint --fix)    npm run dev    Documentation can be found at https://vuejs-templates.github.io/webpack    $  cd vue-test  $  cnpm install  ✔ Installed 58 packages  ✔ Linked 0 latest versions  ✔ Run 0 scripts  ✔ All packages installed (used 237ms(network 227ms), speed 0B/s, json 0(0B), tarball 0B)    # run的時候會根據配置進行webpack靜態資源編譯  $ cnpm run dev   DONE  Compiled successfully in 4388ms    > Listening at http://localhost:8080

當使用了cnpm run dev後,即成功運行起來一個前端服務,因此你會看到類似下面的頁面。

vue渲染後端API數據

1. 首先快速看一下vue項目的程式碼結構.

$ tree -L 1 .  .  ├── README.md  ├── build  ├── config  ├── index.html  ├── node_modules  ├── package.json  ├── src  ├── static  └── test  # 對於快速開發而言,只需要知道src目錄下為vue相關的程式碼,即我們看到vue的歡迎頁面就是src下的    $ tree -L 2 src  src  ├── App.vue  ├── assets  │   └── logo.png  ├── components  │   └── HelloWorld.vue  ├── main.js  └── router      └── index.js

注意: 可以看到一個vue項目的源碼部分由這麼幾個部分組成

  • js主文件main.js
  • vue主文件App.vue
  • 靜態文件目錄assets
  • 自定義組件components
  • 路由目錄router

我們首先來看一下App.vue程式碼

# 我們可以看到在div 這裡有個img標籤,這裡其實就是我們剛才看到歡迎頁面的vue的logo  # 其實可以看到使用了<router-view>標籤,這裡其實是定義了默認的組件,也就是下面導入的HelloWorld  $ cat App.vue  <!--展示模板-->  <template>    <!--這裡用的是id選擇器來綁定css樣式的-->    <div id="app">      <img src="./assets/logo.png">      <router-view></router-view>    </div>  </template>    <script>  import HelloWorld from './components/HelloWorld'  export default {    name: 'helloworld',    components: {      HelloWorld    }  }  </script>  <!--樣式程式碼-->  <style>  #app {    font-family: 'Avenir', Helvetica, Arial, sans-serif;    -webkit-font-smoothing: antialiased;    -moz-osx-font-smoothing: grayscale;    text-align: center;    color: #2c3e50;    margin-top: 60px;  }  </style>

我們再來查看一下components/HelloWorld.vue文件:

# 其實就是我們剛才看到的歡迎頁下面的一些超鏈接  $ cat components/HelloWorld.vue  <template>    <div class="HelloWorld">      <h1>{{ msg }}</h1>      <h2>Essential Links</h2>      <ul>        <li>          <a            href="https://vuejs.org"            target="_blank"          >            Core Docs          </a>        </li>        <li>          <a            href="https://forum.vuejs.org"            target="_blank"          >            Forum          </a>        </li>        <li>          <a            href="https://chat.vuejs.org"            target="_blank"          >            Community Chat          </a>        </li>        <li>          <a            href="https://twitter.com/vuejs"            target="_blank"          >            Twitter          </a>        </li>    .........

其實到這裡,我們基本就知道了整個vue項目是如何把資源渲染出來的。不過我們再來看一下router下的定義。

# 其實就是定義我們如何能訪問到這個資源  $ cat router/index.js  import Vue from 'vue'  import Router from 'vue-router'  import HelloWorld from '@/components/HelloWorld'  Vue.use(Router)    export default new Router({    routes: [      {        path: '/',        name: 'HelloWorld',        component: HelloWorld      }    ]  })

2. 思考我們接下來要做什麼

現在我們知道vue是如何渲染的相關數據,並且知道了大概的編碼規則,但是我們的數據並不在本地,而是一個對外API,此時我們需要想辦法讓vue獲取到後端的數據。

沒錯,這個時候,我們需要一些非同步請求的方式讓vue拿到數據,比如ajax之類的,不過在大前端時代,有更好的工具,即axios ,接下來在我們的vue環境中安裝axios環境:

# 安裝非同步請求包  $ cnpm install --save axios

3. vue渲染後端數據

模擬編寫一個components/HelloWorld組件

# 編寫一個ApiData.vue的組件  $ cat components/ApiData.vue  <template>    <!--使用class來綁定css的樣式文件-->    <div class="hello">      <!--{{}} 輸出對象屬性和函數返回值-->      <h1>{{ msg }}</h1>      <h1>site : {{site}}</h1>      <h1>url : {{url}}</h1>      <h3>{{details()}}</h3>      <h1 v-for="data in ydata" :key="data">{{data}}</h1>      <h3 v-for="item in xdata" :key="item">{{item}}</h3>    </div>  </template>    <script>  import axios from 'axios'  export default {    name: 'apidata',    // data用來定義返回數據的屬性    data () {      return {        msg: 'hello,xxbandy!',        site: "bgops",        url: "https://xxbandy.github.io",        xdata: null,        ydata: null,      }    },    // 用於定義js的方法    methods: {      details: function() {        return this.site      },    },    mounted () {        // response返回一個json{"data": "數據","status": "狀態碼","statusText":"狀態文本","headers":{ "content-type": "application/json; charset=utf-8" },"config":"配置文件","method":"方法","url":"請求url","request":"請求體"}        axios.get('http://localhost:8000/v1/line').then(response => (this.xdata = response.data.legend_data,this.ydata = response.data.xAxis_data))      }  }  </script>    <!--使用css的class選擇器[多重樣式的生效優先順序]-->  <style>  .hello {    font-weight: normal;    text-align:center;    font-size:8pt;  }  h3  {    text-align:center;    font-size:20pt;    color:red;  }  </style>

在路由中增加我們的components

# 增加路由  cat router/index.js  import Vue from 'vue'  import Router from 'vue-router'  import HelloWorld from '@/components/HelloWorld'  // 增加我們自定義的ApiData組件  import Hello from '@/components/ApiData'  Vue.use(Router)    export default new Router({    routes: [      {        path: '/',        name: 'HelloWorld',        component: HelloWorld      },      // 在這裡引用我們的組件      {        path: '/xxb',        name: 'Hello',        component: Hello      }    ]  })

App.vue文件中定義我們的vue腳本

# 增加如下內容  <script>  import Hello from './components/ApiData'  export default {    name: 'xxb',    components: {      Hello    }  }  </script>

運行服務

此時,我們可以運行服務,來檢測我們的程式。

# 在vue的項目家目錄下運行(由於我們的golang的api介面運行的是8000埠,因此vue的埠需要修改一下)  $ cnpm run dev  Your application is running here: http://localhost:8082

此時,我們就可以看到vue成功將後端Golang的API數據進行渲染出來了。雖然只是簡單渲染,但,基本上已經實現了後端API和前端vue項目的融合。接下來就需要根據需求繼續改造了。