使用Go语言和Vue.js的简单Todo项目教程

这是一个使用Go语言结合Vue.js的简单Todos项目,可以用来学习Go与Vue.js。

使用条件:
1.使用了Echo框架. Echo是类似Slim PHP 或 Lumen.的Go微框架,通过路由处理HTTP请求。
2.使用SQLite 数据库。

开发步骤:
1. 下载echo和SQLite:
$ go get github.com/labstack/echo
$ go get github.com/mattn/go-sqlite3

2.创建项目目录:
$ cd $GOPATH/src
$ mkdir go-echo-vue && cd go-echo-vue

3.创建todo.go文件:


// todo.go
package main

import (
"github.com/labstack/echo"
"github.com/labstack/echo/engine/standard"
)

创建main函数:


// todo.go
func main() { }

为了让VueJS能够和后端通讯,创建基本路由,首先创建Echo新实例,使用内建函数定义简单路由规则。
在main函数中写入这些规则:


// todo.go
func main() {
// Create a new instance of Echo
e := echo.New()

e.GET(
"/tasks", func(c echo.Context) error { return c.JSON(200, "GET Tasks") })
e.PUT(
"/tasks", func(c echo.Context) error { return c.JSON(200, "PUT Tasks") })
e.DELETE(
"/tasks/:id", func(c echo.Context) error { return c.JSON(200, "DELETE Task "+c.Param("id")) })

// Start as a web server
e.Run(standard.New(
":8000"))
}

启动:
$ go build todo.go
$ ./todo

为了测试路由,使用Chrome插件Postman.打开Postman,指向localhost:8000,使用各种HTTP动词测试/tasks

下面是初始化数据库,指定文件名为storage.db:


// todo.go
package main
import (
"database/sql"

"github.com/labstack/echo"
"github.com/labstack/echo/engine/standard"
_
"github.com/mattn/go-sqlite3"
)


在main函数加入:


// todo.go
func main() {

db := initDB(
"storage.db")
migrate(db)

定义创建数据库和迁移数据表,initDB是打开db文件,如果不存在创建它。migrate是运行SQL创建Todo数据表。


// todo.go
func initDB(filepath string) *sql.DB {
db, err := sql.Open(
"sqlite3", filepath)

// Here we check for any db errors then exit
if err != nil {
panic(err)
}

// If we don't get any errors but somehow still don't get a db connection
// we exit as well
if db == nil {
panic(
"db nil")
}
return db
}

func migrate(db *sql.DB) {
sql := `
CREATE TABLE IF NOT EXISTS tasks(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR NOT NULL
);
`

_, err := db.Exec(sql)
// Exit if something goes wrong with our SQL statement above
if err != nil {
panic(err)
}
}

再次启动:
$ go build todo.go
$ ./todo

应该看到数据库文件存在目录,使用SQLite管理命令行进入可查看到数据库。

最后我们创建处理器Handler和Model,将路由接受和数据库连接起来。

Handler部分:
导入库:


package main
import (
"database/sql"
"go-echo-vue/handlers"

"github.com/labstack/echo"
"github.com/labstack/echo/engine/standard"
_
"github.com/mattn/go-sqlite3"
)

用将创建的新handler设置路由:


// todo.go
e := echo.New()

e.File(
"/", "public/index.html")
e.GET(
"/tasks", handlers.GetTasks(db))
e.PUT(
"/tasks", handlers.PutTask(db))
e.DELETE(
"/tasks/:id", handlers.DeleteTask(db))

e.Run(standard.New(
":8000"))
}

Handler代码:


// handlers/tasks.go
package handlers

import (
"database/sql"
"net/http"
"strconv"

"github.com/labstack/echo"
)

// handlers/tasks.go

// GetTasks endpoint
func GetTasks(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
return c.JSON(http.StatusOK,
"tasks")
}
}

// PutTask endpoint
func PutTask(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
return c.JSON(http.StatusCreated, H{
"created": 123,
}
}

// DeleteTask endpoint
func DeleteTask(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
id, _ := strconv.Atoi(c.Param(
"id"))
return c.JSON(http.StatusOK, H{
"deleted": id,
})
}
}

Model部分代码,包括业务模型和数据库CRUD:


// models/tasks.go
package models

import (
"database/sql"

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

// Task is a struct containing Task data
type Task struct {
ID int `json:
"id"`
Name string `json:
"name"`
}

// TaskCollection is collection of Tasks
type TaskCollection struct {
Tasks []Task `json:
"items"`
}

func GetTasks(db *sql.DB) TaskCollection {
sql :=
"SELECT * FROM tasks"
rows, err := db.Query(sql)
// Exit if the SQL doesn't work for some reason
if err != nil {
panic(err)
}
// make sure to cleanup when the program exits
defer rows.Close()

result := TaskCollection{}
for rows.Next() {
task := Task{}
err2 := rows.Scan(&task.ID, &task.Name)
// Exit if we get an error
if err2 != nil {
panic(err2)
}
result.Tasks = append(result.Tasks, task)
}
return result
}

func PutTask(db *sql.DB, name string) (int64, error) {
sql :=
"INSERT INTO tasks(name) VALUES(?)"

// Create a prepared SQL statement
stmt, err := db.Prepare(sql)
// Exit if we get an error
if err != nil {
panic(err)
}
// Make sure to cleanup after the program exits
defer stmt.Close()

// Replace the '?' in our prepared statement with 'name'
result, err2 := stmt.Exec(name)
// Exit if we get an error
if err2 != nil {
panic(err2)
}

return result.LastInsertId()
}

func DeleteTask(db *sql.DB, id int) (int64, error) {
sql :=
"DELETE FROM tasks WHERE id = ?"

// Create a prepared SQL statement
stmt, err := db.Prepare(sql)
// Exit if we get an error
if err != nil {
panic(err)
}

// Replace the '?' in our prepared statement with 'id'
result, err2 := stmt.Exec(id)
// Exit if we get an error
if err2 != nil {
panic(err2)
}

return result.RowsAffected()
}

将Model插入Handler代码:


// handlers/tasks.go
package handlers

import (
"database/sql"
"net/http"
"strconv"

"go-echo-vue/models"

"github.com/labstack/echo"
)

// handlers/tasks.go

// GetTasks endpoint
func GetTasks(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
// Fetch tasks using our new model
return c.JSON(http.StatusOK, models.GetTasks(db))
}
}

// PutTask endpoint
func PutTask(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
// Instantiate a new task
var task models.Task
// Map imcoming JSON body to the new Task
c.Bind(&task)
// Add a task using our new model
id, err := models.PutTask(db, task.Name)
// Return a JSON response if successful
if err == nil {
return c.JSON(http.StatusCreated, H{
"created": id,
})
// Handle any errors
} else {
return err
}
}
}

// DeleteTask endpoint
func DeleteTask(db *sql.DB) echo.HandlerFunc {
return func(c echo.Context) error {
id, _ := strconv.Atoi(c.Param(
"id"))
// Use our new model to delete a task
_, err := models.DeleteTask(db, id)
// Return a JSON response on success
if err == nil {
return c.JSON(http.StatusOK, H{
"deleted": id,
})
// Handle errors
} else {
return err
}
}
}

以上都是Go后端代码。待续:

,下面是VueJS前端:
这里index.html代码


<!-- public/index.html -->

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">

<title>TODO App</title>

<!-- Latest compiled and minified CSS -->
<link rel=
"stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

<!-- Font Awesome -->
<link rel=
"stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">

<!-- JQuery -->


<!-- Latest compiled and minified JavaScript -->


<!-- Vue.js -->


</head>
<body>
<div class=
"container">
<div class=
"row">
<div class=
"col-md-4">
<h2>My Tasks</h2>
<ul class=
"list-group">
<li class=
"list-group-item" v-for="task in tasks">
{{ task.name }}
<span class=
"pull-right">
<button class=
"btn btn-xs btn-danger" v-on:click="deleteTask($index)">
<i class=
"fa fa-trash-o" aria-hidden="true"></i>
</button>
</span>
</li>
</ul>
<div class=
"input-group">
<input type=
"text"
class=
"form-control"
placeholder=
"New Task"
v-on:keyup.enter=
"createTask"
v-model=
"newTask.name">
<span class=
"input-group-btn">
<button class=
"btn btn-primary" type="button" v-on:click="createTask">Create</button>
</span>
</div><!-- /input-group -->
</div>
</div>
</div>
</body>
</html>

与后端通讯的JS代码:



new Vue({
el: 'body',

data: {
tasks: [],
newTask: {}
},

// This is run whenever the page is loaded to make sure we have a current task list
created: function() {
// Use the vue-resource $http client to fetch data from the /tasks route
this.$http.get('/tasks').then(function(response) {
this.tasks = response.data.items ? response.data.items : []
})
},

methods: {
createTask: function() {
if (!$.trim(this.newTask.name)) {
this.newTask = {}
return
}

// Post the new task to the /tasks route using the $http client
this.$http.put('/tasks', this.newTask).success(function(response) {
this.newTask.id = response.created
this.tasks.push(this.newTask)
console.log(
"Task created!")
console.log(this.newTask)
this.newTask = {}
}).error(function(error) {
console.log(error)
});
},

deleteTask: function(index) {
// Use the $http client to delete a task by its id
this.$http.delete('/tasks/' + this.tasks[index].id).success(function(response) {
this.tasks.splice(index, 1)
console.log(
"Task deleted!")
}).error(function(error) {
console.log(error)
})
}
}
})

至此代码完成。
运行:
$ go build todo.go
$ ./todo

浏览器打开http://localhost:8000.


源码案例下载安装:
1. git clone https://github.com/skadimoolam/go-vue-todos
2. cd go-vue-todos
3. go get -u github.com/labstack/echo -- This installs ECHO
4. go run todo.go
5.浏览器打开 localhost:8080


GitHub - skadimoolam/go-vue-todos: Simple Todos us

我还是更喜欢vue