前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再来一篇,Go+Vue前后端分离设计实践

再来一篇,Go+Vue前后端分离设计实践

原创
作者头像
闫同学
修改2023-09-23 15:27:37
1.2K0
修改2023-09-23 15:27:37
举报
文章被收录于专栏:扯编程的淡

在之前我曾写过一篇文章《手把手教你搭建Spring Boot+Vue前后端分离》,讲述了如何使用当下流行的Java后端框架Spring Boot和前端框架Vue来进行前后端分离设计,以及什么是前后端分离、跨越问题和设计流程等等,当时还是一名妥妥的Javaer,可是时过境迁,现在的我已然是一名十分活跃的Gopher,成为Gopher一段时间之后再回头看Java的代码,有三个问题甚是不解:

  • 怎么会有try...catch...这种东西?
  • 为什么每一行末尾一定要加分号...
  • 我的指针呢!?

哈哈开个玩笑,下面我们回归正题。

本次这篇文章可以称为是上一篇文章的姊妹篇,使用更加简洁的Go语言和它的Gin框架来设计后端,进行一次Go+Vue前后端分离实践,希望能够对Go语言的初学者起到良好的参考作用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gscZi00q-1682857177423)(一文搞懂Go+Vue前后端分离设计实践.assets/image-20230430160218534.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gscZi00q-1682857177423)(一文搞懂Go+Vue前后端分离设计实践.assets/image-20230430160218534.png)]

这个是更新后的架构图,当然网络上可能会有更加详细的,大家可以先进行大致的了解。

1 为什么要进行前后端分离设计

答:一切都是为了解耦合!

前后端分离设计是一种软件开发方式,它将前端和后端设计为两个独立的模块,分别由不同的团队进行开发和维护。这种设计模式可以带来一些好处,包括:

  1. 提高代码质量:前后端分离可以使开发人员专注于实现业务逻辑,从而提高代码质量和可维护性。
  2. 减少耦合性:前后端分离可以减少代码之间的耦合性,从而减少后期维护的难度。
  3. 更好的测试和部署:前后端分离可以使得测试和部署更加容易,因为前后端代码是独立的。
  4. 提高性能:前后端分离可以加速页面加载速度,因为前端性能瓶颈通常出现在后端。

当然,前后端分离设计也存在一些挑战和限制,例如:

  1. 开发成本:前后端分离需要更多的开发人员和技术支持,因此可能会增加开发成本。
  2. 部署复杂性:前后端分离需要更多的配置和管理工作,因此可能会增加部署复杂性。
  3. 维护成本:前后端分离可能会增加维护成本,因为需要更多的人员来维护不同的代码版本和平台。

2 Go+Vue前后端分离

Go+Vue前后端分离也十分类似于Spring Boot+Vue前后端分离,因为他们本质上都是属于前后端分离设计,而且都有一个相同的约定,那就是Rustful的API风格和JSON数据格式进行数据传输,因此对于前端来说都是一样的接口,所以本次使用相同的前端代码,使用Go的Gin框架来重构后端代码,总体上也可分为以下大致几步:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VgRNDEfC-1682857177424)(一文搞懂Go+Vue前后端分离设计实践.assets/image-20230430195713075.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VgRNDEfC-1682857177424)(一文搞懂Go+Vue前后端分离设计实践.assets/image-20230430195713075.png)]

Go后端需要的依赖如下:

代码语言:shell
复制
github.com/gin-gonic/gin v1.9.0
github.com/go-sql-driver/mysql v1.7.1
github.com/spf13/cast v1.5.0

3 部分源码分享

3.1 加载配置

首先进行对MySQL连接驱动的初始化操作

config.go

代码语言:go
复制
var Db *sql.DB

const (
	DbDriver   = "mysql"
	DbUserName = "root"
	DbPasswd   = "12345"
	DbHost     = "127.0.0.1"
	DbPort     = 3306
	DbName     = "test"
)

func InitDB() {
	var err error
	Db, err = sql.Open(DbDriver, fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",
		DbUserName, DbPasswd, DbHost, DbPort, DbName))
	if err != nil {
		panic(err)
	}
	Db.SetConnMaxLifetime(100 * time.Second)
	Db.SetMaxOpenConns(10)
	Db.SetMaxIdleConns(16)
}
3.2 数据模型

因为Go语言非常的简介,因此我们就将数据模型和对数据模型的数据库操作放在一个文件中

student.go

代码语言:go
复制
type Student struct {
	Id   int32  `json:"id"`
	Name string `json:"name"`
	Age  int8   `json:"age"`
}

var DefaultStudentDb *StudentDb

func NewStudentDb() *StudentDb {
	return &StudentDb{TbName: "student", Db: config.Db}
}

type StudentDb struct {
	TbName string
	Db     *sql.DB
}

func (s *StudentDb) Save(student Student) error {
	stmt, err := s.Db.Prepare(fmt.Sprintf("INSERT INTO %s(name,age) VALUES(?,?)", s.TbName))
	if err != nil {
		return err
	}
	if _, err = stmt.Exec(student.Name, student.Age); err != nil {
		return err
	}
	return nil
}

func (s *StudentDb) GetAll() ([]Student, error) {
	students := make([]Student, 0)
	rows, err := s.Db.Query(fmt.Sprintf("SELECT id,name,age from %s", s.TbName))
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		var student Student
		if err := rows.Scan(&student.Id, &student.Name, &student.Age); err != nil {
			return nil, err
		}
		students = append(students, student)
	}
	return students, nil
}

func (s *StudentDb) GetOne(id int32) (Student, error) {
	var student Student
	rows, err := s.Db.Query(fmt.Sprintf("SELECT id,name,age from %s WHERE id=%d", s.TbName, id))
	if err != nil {
		return student, err
	}
	if rows.Next() {
		if err := rows.Scan(&student.Id, &student.Name, &student.Age); err != nil {
			return student, err
		}
	}
	return student, nil
}

func (s *StudentDb) Remove(id int32) error {
	stmt, err := s.Db.Prepare(fmt.Sprintf("DELETE FROM %s WHERE id=%d", s.TbName, id))
	if err != nil {
		return err
	}
	if _, err := stmt.Exec(); err != nil {
		return err
	}
	return nil
}

func (s *StudentDb) Update(student Student) error {
	stmt, err := s.Db.Prepare(fmt.Sprintf("UPDATE %s SET name=?,age=? WHERE id=?", s.TbName))
	if err != nil {
		return err
	}
	if _, err := stmt.Exec(student.Name, student.Age, student.Id); err != nil {
		return err
	}
	return nil
}
3.3 API设计

基于Gin框架的HTTP Rustful API设计

student.go

代码语言:go
复制
func Update(c *gin.Context) {
	student := model.Student{}
	if err := c.BindJSON(&student); err != nil {
		fmt.Println(err)
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	if err := model.DefaultStudentDb.Update(student); err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	c.JSON(http.StatusOK, 1)
}

func Remove(c *gin.Context) {
	id := c.Param("id")
	if err := model.DefaultStudentDb.Remove(cast.ToInt32(id)); err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	c.JSON(http.StatusOK, 1)
}

func FindById(c *gin.Context) {
	id := c.Param("id")
	student, err := model.DefaultStudentDb.GetOne(cast.ToInt32(id))
	if err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	c.JSON(http.StatusOK, student)
}

func FindAll(c *gin.Context) {
	students, err := model.DefaultStudentDb.GetAll()
	if err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	c.JSON(http.StatusOK, students)
}

func Save(c *gin.Context) {
	student := model.Student{}
	if err := c.BindJSON(&student); err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	if err := model.DefaultStudentDb.Save(student); err != nil {
		c.JSON(http.StatusInternalServerError, -1)
		return
	}
	c.JSON(http.StatusOK, 1)
}

router.go

代码语言:go
复制
const (
	Port = 8081
)

func RunHttp() error {
	r := gin.Default()
	r.Use(CorsConfig())
	router := r.Group("/student")
	{
		router.POST("/save", Save)
		router.GET("/findAll", FindAll)
		router.GET("/findById/:id", FindById)
		router.POST("/update", Update)
		router.DELETE("/remove/:id", Remove)
	}
	return r.Run(fmt.Sprintf("127.0.0.1:%d", Port))
}

//解决跨域
func CorsConfig() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
		c.Header("Access-Control-Allow-Headers", "*")
		c.Header("Access-Control-Expose-Headers", "*")
		c.Header("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Max-Age", "86400")
		if c.Request.Method == http.MethodOptions {
			c.AbortWithStatus(200)
		} else {
			c.Next()
		}
	}
}
3.4 启动文件

main.go

代码语言:go
复制
package main

import (
	"back_go/config"
	"back_go/model"
	"back_go/web"
)

func main() {
	config.InitDB()
	model.DefaultStudentDb = model.NewStudentDb()
	web.RunHttp()
}

4 总结和注意点

大家如果看过上一篇文章《手把手教你搭建Spring Boot+Vue前后端分离》的话相信你会感觉到Go相比Java在编码层面确实简洁了不少,也正是因此我才一点点的喜欢上Go语言,但是Java强大的生态体系是很难撼动的,就比如在使用Spring Boot搭建后端服务的时候,针对配置文件的读取、ORM框架的整合等等,Go语言的相关框架的整合确实不是十分的方便,当然如果要实现非常简单的代码,没有框架就是最好的框架。

在Go后端项目的配置方面,config.go文件中可以配置数据库相关信息,router.go文件中可以配置HTTP启动端口号相关信息,当然也可以写到相关的配置文件中,由Viper等工具进行解析。

5 代码获取方式

关于代码获取方式目前已经上传到GitHub,大家可以关注公众号【扯编程的淡】回复关键字【前后端】获取

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 为什么要进行前后端分离设计
  • 2 Go+Vue前后端分离
  • 3 部分源码分享
    • 3.1 加载配置
      • 3.2 数据模型
        • 3.3 API设计
          • 3.4 启动文件
          • 4 总结和注意点
          • 5 代码获取方式
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档