使用 Vue+Go 进行 Web 开发的笔记

前言

最近一直在使用 Vue3 和 Golang 进行 Web 开发,中间也遇到了比较多的问题。因此特地开一篇笔记来记录一下,我所遇到的一些问题以及我的解决方案。

该篇笔记会分为三大部分:一是 Vue3+element-plus 中所遇到的问题;二是 Go 中所遇到的问题;三便是其他的一些问题,不能归属到前面两种类型,但又与开发密不可分的问题。

希望这篇笔记能帮助到未来前来考古的我。

Vue3 篇

Vue3 在目前这个时间点处于正式发布了一段时间的阶段,同时 element-plus 也是。

读取 Go 回传的时间类型

在使用 Go 作为 Web 后端的时候,如果有需要用到时间的地方为了方便,一般都是直接使用 time.Time 类型来存储。这就导致如果将该类型直接通过序列化 JSON 的方式向前端传输时,前端接收到的时间格式会变成形如 2022-11-10T15:54:47+08:00 的 RFC3339 字符串。若直接显示这个数据,会造成显示效果过于紧凑不美观。因此需要将其转换成形如 2022-11-10 15:54:47 的字符串。

1
2
3
4
5
6
7
8
9
10
# 假设传入的数据为iterator.Time
var date = new Date(iterator.Time).toJSON();
var newDate = new Date(+new Date(date) + 8 * 3600 * 1000).toISOString().replace(/T/g, ' ').replace(/\.[\d]{3}Z/, '');

# 也可以将其包装成一个函数来调用
const goTimeToStr = (oldTime string) => {
var date = new Date(oldTime).toJSON();
var newDate = new Date(+new Date(date) + 8 * 3600 * 1000).toISOString().replace(/T/g, ' ').replace(/\.[\d]{3}Z/, '');
return newDate;
}

数据表分页

数据表分页目前 Vue3 下的实现方式已经非常简单了。同时为了省事和提高执行效率,在进行数据分页的同时也将查询内容进行分页,避免后端一次性查询过多的内容,影响数据库性能。

Vue3 中存在一个 Pagination 分页组件,能够显示一个页码栏。详细文档可以参照:Pagination 分页 | Element Plus

这里主要需要用到他的一个 current-change 事件,用于监听用户切换页面的行为。该事件会传入跳转到的新页码,类型为 number,因此可以使用如下的函数,并将其绑定到 current-change 上。

1
2
3
4
const handleCurrentChange = (newPage: number) => {
console.log("页面改变了哦,新的页码是:", newPage)
getData(newPage) // 拉取指定页码的数据
}

前端的数据基于开头的缘由,也只是存储和展示当前页码的数据,因此在 Html 中直接插入一个表格即可

1
2
3
4
5
<!-- tableData 为当前页面的数据,由 getData 函数拉取 -->
<el-table :data="tableData" border style="width: 100%" :row-class-name="agentAddCoinRecordList">
<el-table-column prop="time" label="时间" />
<el-table-column prop="status" label="状态" />
</el-table>

另外这个分页组件的默认位置是靠左的。如果想要将页码居中,可以加上下面的样式

1
2
3
.el-pagination {
justify-content: center;
}

Go 篇

数据库查询分页

由于前端数据分页展示的需求,同时考虑到数据量过大时查询会导致后端出现可感知的处理延时,因此后端在查询数据的时候也没必要一次性返回所有数据了。

这里为了快速构建应用和规范化数据库操作,使用了 ORM 框架:GORM - The fantastic ORM library for Golang, aims to be developer friendly. 该框架支持使用 Scopes 来复用一些逻辑,官方给出的示例逻辑可以参考:Scopes | GORM - The fantastic ORM library for Golang, aims to be developer friendly. 这里我使用的是分页:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
q := r.URL.Query()
page, _ := strconv.Atoi(q.Get("page"))
if page == 0 {
page = 1
}

pageSize, _ := strconv.Atoi(q.Get("page_size"))
switch {
case pageSize > 100:
pageSize = 100
case pageSize <= 0:
pageSize = 10
}

offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}

在使用的时候,可以像这样来用:

1
2
3
4
5
// 查询内容
rows, err := db.Scopes(Paginate(c.Request)).Model(&AddCoinRecords{}).Where("object_type = ?", isPlayer).Order("time DESC").Rows()
// 总数据量
var count int64
db.Model(&AddCoinRecords{}).Where("object_type = ?", isPlayer).Count(&count)

在上面的代码中,和常规的非分页查询相比只是增加了 .Scopes(Paginate(c.Request)) 。里面用到的 c.Request 实际上是 gin.Context 中存入的传入查询参数。

为了给前端回传所有符合要求的数据量(用于计算剩余页数),所以下面还增加了一个 .Count(&count) 来获取数据量。

其他

这里放置的是一些与本次 Web 开发相关,但并不归属于上面的内容。例如一些环境的搭建之类的问题,就会在这里提到一下。

Docker 启动数据库乱码的解决

为了方便部署数据库,我使用了 Docker 的方式进行快速配置和启动。具体的启动方式可以参考这个官方的页面:mysql - Official Image | Docker Hub

但他有一个很严重的问题,那就是在我目前使用的 mysql:5.7 以及更高的版本中,其默认的数据库编码类型为 latin1,这就导致了存入的中文会像下图这样,被映射到拉丁字符去。

数据库所使用的字符集为 latin1

中文乱码

解决起来也比较容易,只需要将数据库的编码设定为 utf8mb4 即可,具体的启动文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '3'

services:
mariadb:
image: mysql:5.7
restart: always
ports:
- 3306:3306
environment:
- LANG=C.UTF-8
volumes:
- /etc/localtime:/etc/localtime
command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']

一般情况下,使用上面的配置文件启动后的数据编码格式就会变为 utf8mb4

修正后的数据库编码

一般情况下,到目前为止已经可以解决数据库中文乱码的问题了。但我目前这个环境多少有点问题,仍然是乱码的。因为数据库存入数据到取出数据会经过如下的转换步骤:

MySQL 各个字符集作用节点

而我这边依然乱码的原因则是第三方存入数据的时候,存入的是 utf8mb4 编码的内容,但存储格式为 utf8。这就导致了在转码的过程中出现了错误。最后我是用了配置文件来进行设置,使得数据库编码变成如下的形式

生产环境所使用的数据库编码

可能你此时会觉得直接配置 --default-character-set 启动参数也可以,但 emmm 在 5.7 版本中用这个的话会出现启动错误,所以是没法用的。