我的Vue之旅 07 Axios + Golang + Sqlite3 實現簡單評論機制

来源:https://www.cnblogs.com/linxiaoxu/archive/2022/10/19/16804756.html
-Advertisement-
Play Games

第三期 · 使用 Vue 3.1 + Axios + Golang + Sqlite3 實現簡單評論機制 效果圖 CommentArea.vue 我們需要藉助js的Data對象把毫秒時間戳轉化成 UTCString() 。併在模板表達式中使用 {{ dateConvert(value.date) } ...


第三期 · 使用 Vue 3.1 + Axios + Golang + Sqlite3 實現簡單評論機制

效果圖

image-20221018233644580


CommentArea.vue

image-20221018233754506

我們需要藉助js的Data對象把毫秒時間戳轉化成 UTCString() 。併在模板表達式中使用 {{ dateConvert(value.date) }}

src="@/assets/avater/hamster.jpg"頭像目前目前是固定的,也可以將頭像資源地址存入資料庫中。

獲取JavaScript時間戳函數的方法和js時間戳轉時間方法_半生過往的博客-CSDN博客_js時間戳轉時間

    dateConvert(date: number): string {
      return new Date(date).toUTCString();
    },

<template>
  <div class="m-2">
    <div class="text-3xl font-bold">Comments</div>
    <template v-if="comments.length == 0">當前pid帖子沒有評論</template>
    <template v-for="(value, index) in comments" :key="index">
      <div class="border border-stone-300 p-1">
        <div>
          <img
            src="@/assets/avater/hamster.jpg"
            class="inline-block w-12 h-12 align-top"
          />
          <div class="inline-block ml-2">
            <div class="font-bold text-stone-700">{{ value.name }}</div>
            <div class="text-stone-400 text-sm">
              {{ dateConvert(value.date) }}
            </div>
          </div>
        </div>
        <div class="mt-2">{{ value.text }}</div>
        <div class="float-right">
          <span class="m-1 text-rose-500">回覆</span>
          <span class="m-1 text-rose-500" @click="deleteComment(value.id)"
            >刪除</span
          >
        </div>
        <div class="clear-both"></div>
      </div>
      <div class="mt-2"></div>
    </template>
  </div>
</template>
<script lang="ts">
import { PropType } from "vue";

interface Comment {
  date: number;
  text: string;
  id: number;
  name: string;
}

export default {
  name: "CommentArea",
  props: {
    comments: {
      type: Array as PropType<Comment[]>,
      required: true,
    },
  },
  methods: {
    dateConvert(date: number): string {
      return new Date(date).toUTCString();
    },
    deleteComment(id: number) {
      this.$emit("delete-comment", id);
    },
  },
};
</script>

Axios

安裝vue-axios

npm install axios vue-axios --save

導入vue-axios

修改 main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { BootstrapIconsPlugin } from 'bootstrap-icons-vue';
import './index.css'
import axios from 'axios'
import VueAxios from 'vue-axios'

axios.defaults.baseURL = '/api'

createApp(App).use(VueAxios, axios).use(BootstrapIconsPlugin).use(store).use(router).mount('#app')

axios.defaults.baseURL = '/api' 用於解決跨域問題

解決跨域問題

修改 vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 8080, //前端服務啟動的埠號
    host: 'localhost', //前端服務啟動後的訪問ip,預設為localhost, host和port組成了前端服務啟動後的訪問入口。
    https: false,
    open: true,
    //以上的ip和埠是我們本機的;下麵為需要跨域的
    proxy: {//配置跨域
        '/api': {
            target: 'http://localhost:1314/',//這裡後臺的地址模擬的;應該填寫你們真實的後臺介面
            ws: true,
            changOrigin: true,//允許跨域
            pathRewrite: {
                '^/api': ''//請求的時候使用這個api就可以
            }
        }
    }
  }
})

CommentTestView.vue

<template>
  <div class="text-center m-2">評論服務測試</div>
  <div class="m-2">
    <div class="text-3xl font-bold">Query Comments</div>
    <input
      id="pid"
      class="input_text"
      type="text"
      placeholder="輸入帖子id查找評論"
      v-model="pid"
    />
    <input
      type="button"
      value="查詢"
      class="input_button"
      @click="queryComment"
    />
  </div>
  <div class="m-2">
    <div class="text-3xl font-bold">Insert Comments</div>
    <input
      id="uid"
      class="input_text"
      type="text"
      placeholder="當前用戶uid"
      v-model="uid"
    />
    <input
      type="button"
      value="添加"
      class="input_button"
      @click="insertComment"
    />
    <div></div>
    <input
      id="pid"
      class="input_text"
      type="text"
      placeholder="當前帖子pid"
      v-model="pid"
    />
    <div></div>
    <textarea
      id="text"
      class="input_text w-full h-20"
      rows="3"
      cols="40"
      placeholder="評論內容"
      v-model="text"
    />
  </div>
  <comment-area
    :comments="comments"
    @delete-comment="deleteComment"
  ></comment-area>
</template>

將 deleteComment 綁定到commentArea的delete-comment事件上,將 insertComment 、 queryComment 分別綁定到兩個按鈕的click事件上。

insertComment 成功執行將拿到插入的評論json對象並放入當前數組中。

deleteComment 成功執行將通過數組的filter函數刪除當前評論json對象。

下方代碼相比前幾期多了style代碼塊,可以將相同標簽使用的共同功能類組合提取出來(兩個按鈕,五個輸入框),簡化代碼。

<script>
import CommentArea from '@/components/common/CommentArea.vue';
export default {
  components: { CommentArea },
  name: 'CommentTestView',
  data: function () {
    return {
      pid: 100,
      uid: 1003,
      text: "",
      comments: [
        // {
        //   id: 1,
        //   uid: 1001,
        //   name: "西紅柿炒芹菜",
        //   text: "真的很不錯啊。SQLite 是一個開源的嵌入式關係資料庫,實現自包容、零配置、支持事務的 SQL 資料庫引擎。",
        //   date: 1665912139673,
        //   img: require("@/assets/avater/hamster.jpg")
        // }
      ]
    }
  },
  methods: {

    insertComment() {
      const params = new URLSearchParams();
      params.append('uid', this.uid)
      params.append('pid', this.pid)
      params.append('text', this.text)
      this.axios.post("insertComment", params
      ).then(response => {
        console.log(response.data)
        this.comments.unshift(
          response.data
        )
        console.log(this.comments)
      }).catch(err => {
        console.log(err)
      })
    },
    deleteComment(id) {
      const params = new URLSearchParams();
      params.append('id', id)
      this.axios.post("deleteComment", params).then(response => {
        console.log(response.data)
        this.comments = this.comments.filter(elem => {
          return elem.id != id
        })
      }).catch(err => {
        console.log(err)
      })
    },
    queryComment() {
      this.axios.get("queryComment", {
        params: {
          pid: this.pid
        }
      }).then(response => {
        if (!response.data) {
          this.comments = []
          return
        }
        this.comments = response.data
        this.comments.reverse()
      }).catch(err => {
        console.log(err)
      })
    }
  },
  created() {
    let old = localStorage.getItem(`comment_${this.pid}`)
    if (old) {
      this.text = old
    }
  },
  watch: {
    text() {
      localStorage.setItem(`comment_${this.pid}`, this.text)
    }
  }
}
</script>

<style scoped>
.input_text {
  @apply mt-2
        inline-block
        bg-white
        focus:outline-none focus:ring focus:border-blue-200
        py-1.5
        pl-3
        border border-stone-400
        text-sm;
}
.input_button {
  @apply border border-rose-400
        text-sm
        font-bold
        text-rose-500
        rounded-sm
        px-4
        py-1
        mt-2
        ml-4
        active:bg-rose-400 active:text-white;
}
</style>

請求體編碼

axios post 請求客戶端可以直接發嗎,不能!在這裡使用了URLSearchParams對象以application/x-www-form-urlencoded格式發送數據。

const params = new URLSearchParams();
params.append('uid', this.uid)
params.append('pid', this.pid)
params.append('text', this.text)

其他方式可看 請求體編碼 | Axios Docs (axios-http.com)


保存沒寫完的評論

寫到一半關閉頁面後重新打開就不在了,可以用 localStorage 本地存儲臨時保存寫的內容,只能保存字元串。

  created() {
    let old = localStorage.getItem(`comment_${this.pid}`)
    if (old) {
      this.text = old
    }
  },
  watch: {
    text() {
      localStorage.setItem(`comment_${this.pid}`, this.text)
    }
  }

創建資料庫和表

使用 Navicat Premium 創建資料庫跟表

image-20221019000127781

image-20221019000137059

image-20221019000156217


Golang 服務端

C:.
│   comment.json
│   go.mod
│   go.sum
│   main.go
│   
├───data
│       data.db
│       
└───lib
    ├───http
    │       server.go
    │       
    ├───mysql
    └───sqlite
            sq3_comment.go
            sq3_init.go
            sq3_users.go

JSON2GO

我們把消息JSON格式擬定出來

[
  {
    "id": 1,
    "uid": 1001,
    "name": "小王",
    "text": "看起來很好玩的樣子。",
    "pid": 100,
    "date": 1665908807784
  }
]

JSON 轉GO,JSON轉GO代碼, go json解析 (sojson.com)

type AutoGenerated []struct {
	ID int `json:"id"`
	UID int `json:"uid"`
	Name string `json:"name"`
	Text string `json:"text"`
	Pid int `json:"pid"`
	Date int64 `json:"date"`
}

解決sqlite3 gcc:exec: "gcc": executable file not found in %PATH%

Windows 如果使用 Go 語言使用 sqlite3 時,需要gcd來編譯sqlite3模塊相關c代碼。

解決方法:安裝tdm64-gcc-9.2.0.exe, https://jmeubank.github.io/tdm-gcc/download/


資料庫處理邏輯 sq3_vue包

sq3_init.go

init() 初始化函數獲取main執行目錄,並按操作系統連接文件位置,讀取文件。

package sq3_vue

import (
	"database/sql"
	"os"
	"path"

	_ "github.com/mattn/go-sqlite3"
)

var db *sql.DB

func init() {
	p, err := os.Getwd()
	checkError(err)
	db, err = sql.Open("sqlite3", path.Join(p, "data/data.db"))
	checkError(err)
}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

sq3_comment.go

為具體的資料庫處理邏輯,插入返回comment的json位元組切片 {},查詢返回comment數組的json位元組切片 [{},{},{}]

*sql.DB 是Go標準庫規定的介面,方便操作。

stmt、rows 需要 defer close()

package sq3_vue

import (
	"encoding/json"
	"fmt"
	"time"
)

type Comment struct {
	ID   int    `json:"id"`
	UID  int    `json:"uid"`
	Name string `json:"name"`
	Text string `json:"text"`
	Pid  int    `json:"pid"`
	Date int64  `json:"date"`
}

const insertStmt = `
INSERT INTO comments(uid,text,pid,date) values(?,?,?,?)
`
const lastedStmt = `
select id,uid,text,pid,date,name from comments join users using(uid) where id = ?
`

func (Comment) InsertComment(uid, pid int64, text string) (json_ []byte, err error) {
	stmt, err := db.Prepare(insertStmt)
	checkError(err)
	defer stmt.Close()
	res, err := stmt.Exec(uid, text, pid, time.Now().UnixMilli())
	checkError(err)
	n, err := res.RowsAffected()
	checkError(err)
	if n == 0 {
		return nil, fmt.Errorf("插入失敗")
	}
	n, err = res.LastInsertId()
	checkError(err)
	stmt, err = db.Prepare(lastedStmt)
	checkError(err)
	defer stmt.Close()
	rows, err := stmt.Query(n)
	checkError(err)
	defer rows.Close()
	rows.Next()
	var c Comment
	rows.Scan(&c.ID, &c.UID, &c.Text, &c.Pid, &c.Date, &c.Name)
	checkError(err)
	json_, err = json.Marshal(c)
	checkError(err)
	return json_, nil
}

const deleteStmt = `
delete from comments where id = ?
`

func (Comment) DeleteComment(id int64) error {
	stmt, err := db.Prepare(deleteStmt)
	checkError(err)
	defer stmt.Close()
	res, err := stmt.Exec(id)
	checkError(err)
	n, err := res.RowsAffected()
	checkError(err)
	if n == 0 {
		return fmt.Errorf("刪除失敗")
	}
	return nil
}

const queryStmt = `
select id,uid,text,pid,date,name from comments join users using(uid) where pid = ?
`

func (Comment) QueryComment(pid int64) (json_ []byte, err error) {
	var res []Comment
	stmt, err := db.Prepare(queryStmt)
	checkError(err)
	defer stmt.Close()
	rows, err := stmt.Query(pid)
	checkError(err)
	defer rows.Close()
	for rows.Next() {
		var c Comment
		err = rows.Scan(&c.ID, &c.UID, &c.Text, &c.Pid, &c.Date, &c.Name)
		checkError(err)
		res = append(res, c)
	}
	json_, err = json.Marshal(res)
	checkError(err)
	return
}

簡單HTTP伺服器

server.go

我們分別判斷請求方法,要求刪除和插入只能用post請求,查詢只能用get請求。使用r.ParseForm() 處理表單。

r.Form["uid"] 本質上拿到的字元串數組,需要進行顯式類型轉換。

db "wolflong.com/vue_comment/lib/sqlite" 引入了前面寫的資料庫處理包。因為考慮到不一定要用 sqlite,未來可能會使用 mysql、mongoDB。目前已經強耦合了,即當前http伺服器的實現跟sq3_vue包緊密相關,考慮用介面降低耦合程度。

type comment interface {
	QueryComment(pid int64) (json_ []byte, err error)
	InsertComment(uid, pid int64, text string) (json_ []byte, err error)
	DeleteComment(id int64) error
}

var c comment = db.Comment{}

我們將資料庫行為接收者指派為Comment類型,當該類型實現了三個對應函數簽名的方法就實現了comment介面。此時我們創建一個空Comment類型賦值給comment介面變數。那麼其他資料庫邏輯處理包只要提供實現comment介面的類型對象就好了。換什麼資料庫也影響不到當前HTTP的處理邏輯。

package server

import (
	"fmt"
	"log"
	"net/http"
	"strconv"

	db "wolflong.com/vue_comment/lib/sqlite"
)

type comment interface {
	QueryComment(pid int64) (json_ []byte, err error)
	InsertComment(uid, pid int64, text string) (json_ []byte, err error)
	DeleteComment(id int64) error
}

var c comment = db.Comment{}

func checkError(err error) {
	if err != nil {
		panic(err)
	}
}

func insertComment(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		fmt.Fprintf(w, "Only POST Method")
		return
	}
	r.ParseForm()
	fmt.Println(r.Form)
	// ^ 簡單實現,有待提高健壯性
	uid, err := strconv.Atoi(r.Form["uid"][0])
	checkError(err)
	pid, err := strconv.Atoi(r.Form["pid"][0])
	checkError(err)
	text := r.Form["text"][0]
	inserted, err := c.InsertComment(int64(uid), int64(pid), text)
	if err != nil {
		fmt.Fprintf(w, "Error Insert")
		return
	}
	fmt.Fprint(w, string(inserted))
}

func deleteComment(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		fmt.Fprintf(w, "Only POST Method")
		return
	}
	r.ParseForm()
	fmt.Println(r.Form)
	id, err := strconv.Atoi(r.Form["id"][0])
	checkError(err)
	err = c.DeleteComment(int64(id))
	if err != nil {
		fmt.Fprintf(w, "Error Delete")
		return
	}
	fmt.Fprintf(w, "Success Delete")
}

func queryComment(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		fmt.Fprintf(w, "Only GET Method")
		return
	}
	r.ParseForm()
	fmt.Println(r.Form)
	pid, err := strconv.Atoi(r.Form["pid"][0])
	checkError(err)
	json, err := c.QueryComment(int64(pid))
	if err != nil {
		fmt.Fprintf(w, "Error Delete")
		return
	}
	fmt.Fprint(w, string(json))
}

func StartServer() {
	http.HandleFunc("/insertComment", insertComment)
	http.HandleFunc("/deleteComment", deleteComment)
	http.HandleFunc("/queryComment", queryComment)
	err := http.ListenAndServe(":1314", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

main.go

package main

import (
	"fmt"

	http "wolflong.com/vue_comment/lib/http"
)

func main() {
	fmt.Println("2022年10月16日 https://cnblogs.com/linxiaoxu")
	http.StartServer()
}

資料

SQLite Join | 菜鳥教程 (runoob.com)

使用 SQLite 資料庫 - 使用 Golang 打造 Web 應用程式 (gitbook.io)

mattn/go-sqlite3: sqlite3 driver for go using database/sql (github.com)

sqlite3 package - github.com/mattn/go-sqlite3 - Go Packages

go-sqlite3/simple.go at master · mattn/go-sqlite3 (github.com)

05.3. 使用 SQLite 資料庫 | 第五章. 訪問資料庫 |《Go Web 編程》| Go 技術論壇 (learnku.com)

04.1. 處理表單的輸入 | 第四章. 表單 |《Go Web 編程》| Go 技術論壇 (learnku.com)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 京東雲TiDB SQL層的背景介紹 從總體上概括 TiDB 和 MySQL 相容策略,如下表: SQL層的架構 用戶的 SQL 請求會直接或者通過 Load Balancer 發送到 京東雲TiDB Server,TiDB Server 會解析 MySQL Protocol Packet,獲取請求內 ...
  • 主從原理 master伺服器將數據的改變記錄二進位binlog日誌,當master上的數據發生改變時,則將其改變寫入二進位日誌中;slave伺服器會在一定時間間隔內對master二進位日誌進行探測其是否發生改變, 當master伺服器數據發生變化,則slave開始一個I/O Thread請求mast ...
  • 一、什麼是工作流? 在闡述什麼是工作流之前,先說一下工作流和普通任務的區別,在於依賴視圖。 普通任務本身他只會有自己的dag圖,依賴視圖是無邊界的,不可控的,而工作流則是把整個工作流都展示出來,是有邊界的,可控的,這是工作流的優勢。下麵為大家介紹工作流的相關功能: 01 工作流—功能介紹 ● 虛擬節 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 本文來源:原創投稿;作者:YeJinrong/葉金榮 測試效率提升36% ~ 100%,相當可觀 本文目錄 並行構建索引測試 進一步提高索引構建效率 並 ...
  • 背景 企業資料庫選型規則。 一、資料庫部署形式 隨著硬體發展,指標上會有變化。 部署形式決定了容量上限,計算能力上限,讀寫帶寬上限,RPO,RTO指標,適應場景。 1、分散式部署(例如pg+citus插件) 容量上限:100節點以上,PB級。 計算能力上限:100節點以上,6400核以上。 讀寫帶寬 ...
  • 複製集群 &.主從模式 (讀寫分離) 主從模式原理 集群運作原理 Redis主從複製預設讀寫分離(主寫從讀). 單點故障時,預設的容災機制可以實現快速故障恢復(單點/多點故障). 主從集群說明 (優點) 易擴展 (可以動態的添加增加從機) (優點) 讀寫分離 (主寫從讀) (缺點) 複製延遲 (寫操 ...
  • 本篇記錄我在實現時的思考過程,寫給之後可能遇到困難的我自己也給到需要幫助的人。 寫的比較淺顯,見諒。 在寫項目代碼的時候,需要把Android端的位置信息傳輸到伺服器端,通過Netty達到連續傳輸的效果,如下: 我們可以先來看看百度地圖官方給出的相關代碼 public class MainActiv ...
  • 原文地址:Android自動化測試工具調研 - Stars-One的雜貨小窩 Android測試按測試方式分類,可分為兩種:一種是傳統邏輯單元測試(Junit),另外一種則是UI交互頁面測試。 這裡詳細講解第二種測試。 UI交互頁面測試如果是人工進行,會消耗人力,且不一定按質量進行測試,測試不全面, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...