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

16-10-07 banq
         

这是一个使用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"
)
<p>

创建main函数:

// todo.go
func main() { }
<p>

为了让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"))
}
<p>

启动:

$ 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"
)


<p>

在main函数加入:

// todo.go
func main() {

    db := initDB("storage.db")
    migrate(db)
<p>

定义创建数据库和迁移数据表,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)
    }
}
<p>

再次启动:

$ 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"
)
<p>

用将创建的新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"))
}
<p>

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,
        })
    }
}

<p>

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()
}

<p>

将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
        }
    }
}
<p>

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

         

1
banq
2016-10-07 19:10

,下面是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>
<p>

与后端通讯的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)
                        })
                    }
                }
            })
           
<p>

至此代码完成。

运行:

$ 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

lendo
2016-10-11 01:28

我还是更喜欢vue