手把手教你Vue3+Node.js+Expres+MySQL环境搭建

A kitten
蒋川
B 端产品经理,卡拉云联合创始人
最近更新 2022年01月24日

Vue3 + Node.js + Expres  + MySQL 环境搭建,手把手教你搭建前后端分离的 Web APP

如何才能搭建一套拥有前后端的完整 APP 呢?本文是你成为全栈工程师的入门教程。

我会先教你安装并配置 Vue 3 + Vue Router + axios(前端部分)搭建前端控制页面。然后教你安装配置 node.js + Expres for REST APIs+ MySQL (后端部分),前后端组在一起就是一个完整的 app。

本教程手把手教你搭建一套 「待办清单」app,前端使用 Vue3 ,一共三个页面,首页,todo 列表页,添加删除页;后端使用 node.js + MySQL 搭建。前后端使用 REST API 通讯。读完本教程,你也能写出一个前后端都跑起来,在 PC 端或移动端浏览器上使用的「待办清单」app。

本教程每段代码我都亲手测过,保证百分百没有错误,请打开你的 terminal 跟随本教程一起操作,从这里开始,成为一名全栈工程师。

一.「待办清单」APP 最终完成概览

02-01-app-todolist.jpg

我们搭建的「待办清单」app 包含以下几个功能/页面

1.首页:展示「待办清单」的列表,可点击查看清单详细及状态。

2.首页搜索:首页有搜索框,可搜索清单。

3.新增清单:可以新建一个清单。

4.编辑清单:可对清单内容进行增删改查

02-02-app-todo.jpg

  • 「新增清单页」,提交新任务到数据库

02-03-app-addtodo.jpg

「任务状态更新页」,修改待办任务的详情和状态并提交到数据库中

如果你跟随本教程一步步来,最终你也可以把 Vue3 + node.js 跑起来并发布自己对「待办清单」app

二. 全栈 APP 框架概览 - Vue3 + Node.js + MySQL

「待办清单」整个前后端应用架构图:

03-01-crud-er.jpg

前端部分:Vue.js + Vue Router + Axios

  • Vue.js 3 是整个前端的框架。
  • Vue Router 是分配页面的路由。
  • Components(组件)本教程将带领你写三个组件(功能页)。
  • axios 是一个与后端通过 API 沟通的 HTTP 请求库,它可以用 Get / Post / Put / Delete 请求与后端 API 进行交互。

后端部分:node.js + Express + Sequelize + MySQL

  • node.js 是整个后端的框架
  • 使用 Express 生成
  • Sequelize ORM
  • MySQL

Vue3 前端搭起来太费劲?

试试卡拉云,无需处理任何前端问题,仅需简单拖拽组件,即可直接生成后台系统,简单几行代码快速连接数据库和API,数月工作量降至 2 天。

三. 前端部分 - Vue3 + Vue Router + Axios + Components

03-02-vue3.jpg

我先来讲解一下前端各部分的功能,如果你没看懂也没关系,跟随教程一步一步走,全部完成后再回来看这个部分,你会豁然开朗。

  • App(应用本身):最外层,用户可看到的前端,用户指令从这里进入。
  • Vue Router(路由):路由根据用户指令选择指向三个组件(Components)中的一个。
  • Components(组件):组件是我们写的功能页,本教程带大家一起写三个组件。
  • TodoDataService:用户在组件中 CRUD 数据,数据根据 TodoDataService 中的规则进行组装。
  • axios(API 调取):axios 会根据 TodoDataService 中的规则与后端 API 通讯交换数据。

前端 Vue3 项目结构

03-03-vue-er.jpg

简单讲一下各文件的作用,本教程后文将手把手教你写每一个配置文件及他们所有的源码

  • package.json 包含 4 个主要模块:Vue、Vue-router、axios、bootstrap。
  • 有 3 个组件(功能页面):TodoList、Todo、 AddTodo
  • router.js 为每个组件(功能页面)定义路由。
  • http-common.js 是 axios 初始化信息,标记了与后端 API 通讯的地址
  • TodoDataService 具有向 API 发送 HTTP 请求的方法。
  • vue.config.js 设置 Vue 访问端口

下面,我们正式进入前后端搭建实战环节,一定要跟随本教程一起操作,自己亲手实践是学会的基础,打开你的 terminal ,我们一起来。

Vue.js 3 安装教程及配置

本教程适用于 Win / Linux / MacOS

安装 Vue.js 3 ,使用 npm 安装 vue

sudo npm install -g @vue/cli

04-01-setup-vue3.png

检查是否安装成功,如果没成功可添加 sudo 再试一次。

04-02-vue-v.png

接着我们创建一个 Vue 的项目,运行命令:

vue create vue-kalacloud-com

01-01-vue3-setup.jpg

运行起来,你会看到几个选项,选择 Default ([Vue 3] babel, eslint),我们安装最新的 Vue 3。

安装完成后,会生成一个「vue-kalacloud-com」目录,我们所有的前端代码都会存在这里。

cd vue-kalacloud-com
root@kalacloud.com:~/vue-kalacloud-com$

cd 到这个目录下,接下来我们要开始配置 Vue 的开发环境。

扩展阅读:《Postman 使用教程 - 手把手教你 API 接口测试

在 Vue 3 中安装使用 Bootstrap

Bootstrap 最初是由 Twitter 两名工程师开发的响应式开源框架,简洁优雅,自适应PC和移动端。现在已经是全球使用最受欢迎的独立 CSS 框架之一。

我们先来把它安装到 Vue 中,在「vue-kalacloud-com」根目录执行安装命令:

npm install bootstrap@4.6.0 jquery popper.js

然后打开 vue-kalacloud-com/src/main.js 文件,导入 Bootstrap:

import { createApp } from 'vue'
import App from './App.vue'
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'

特别提示:为了避免奇怪的错误,请完整复制本教程中的全部代码,使用完全替换的方式将教程中的代码粘贴到对应的文件中。

在 Vue 3 中安装使用 Vue Router

Vue Router 是 Vue 的官方路由,与 Vue 深度整合,让构建响应式单页面变得非常简单快捷。

在「vue-kalacloud-com」根目录执行安装 Vue Router 的命令:

npm install vue-router@4

然后在 vue-kalacloud-com/src 文件夹中新建一个文件 router.js 并定义一下 Vue 的路由

(在 Terminal 里,你可以使用 nano router.js 的方式新建 js 文件并打开编辑器粘贴代码)

import { createWebHistory, createRouter } from "vue-router";

const routes =  [
  {
    path: "/",
    alias: "/todo",
    name: "todo",
    component: () => import("./components/TodoList")
  },
  {
    path: "/todo/:id",
    name: "todo-details",
    component: () => import("./components/Todo")
  },
  {
    path: "/add",
    name: "add",
    component: () => import("./components/AddTodo")
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

router.js 中我们为每个页面创建一个路由:

  • path:路由指向的 URL 路径
  • name:路由指向这个页面时使用的名字
  • component:路由调用此页面时,要加在的组件(这个组件我们马上一起动手写)

最后一步,我们打开 vue-kalacloud-com/src/main.js,在这里加上路由信息(最后一行):

import { createApp } from 'vue'
import App from './App.vue'
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'
import router from './router'

createApp(App).use(router).mount('#app')

在 Vue3 中添加导航栏和 Router View

接下来,我们打开 vue-kalacloud-com/src/App.vue 删掉里面全部代码,然后加入咱们自己项目的导航栏和 Router View :

<template>
  <div id="app">
    <nav class="navbar navbar-expand navbar-dark bg-dark">
      <router-link to="/" class="navbar-brand">卡拉云_kalacloud.com</router-link>
      <div class="navbar-nav mr-auto">
        <li class="nav-item">
          <router-link to="/todo" class="nav-link">任务清单</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/add" class="nav-link">新增任务</router-link>
        </li>
      </div>
    </nav>

    <div class="container mt-3">
      <router-view />
    </div>
  </div>
</template>

<script>
export default {
  name: "app"
};
</script>

特别提示:为了避免奇怪的错误,请完整复制本教程中的全部代码,使用完全替换的方式将教程中的代码粘贴到对应的文件中。

在 Vue3 安装 Axios 并初始化 HTTP 客户端

Axios 是一个基于 Promise 的 HTTP 请求库,它用在 node.js 和浏览器里,在本教程中我们使用 Axios 通过 Get / Post / Put / Delete 请求与后端进行交互。

我们先来安装 Axios:

npm install axios

然后,在 vue-kalacloud-com/src/ 文件夹下,创建一个 http-common.js 文件并复制以下代码进去:

import axios from "axios";export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});

这里的 baseURL 是后端的 REST API url ,记得改成你的服务器配置。

如果你是萌新,没看懂,也没有关系,这里不用改。我们将在本教程的「后端」部分,教你配置后端服务器。

让 Vue3 可以通过 axios 发送 HTTP 请求

我们要给 axios 建立一套与后端服务器沟通的规则,告诉 axios 使用这套规则去后端拿那数据。

我们要在 vue-kalacloud-com/src/services/ 创建一个TodoDataService.js

如果没有 services 文件夹,先建一个,然后在文件夹里新建TodoDataService.js 并复制以下代码。

(文件位置:vue-kalacloud-com/src/services/TodoDataService.js

import http from "../http-common";

class TodoDataService {
  getAll() {
    return http.get("/todos");
  }

  get(id) {
    return http.get(`/todos/${id}`);
  }

  create(data) {
    return http.post("/todos", data);
  }

  update(id, data) {
    return http.put(`/todos/${id}`, data);
  }

  delete(id) {
    return http.delete(`/todos/${id}`);
  }

  deleteAll() {
    return http.delete(`/todos`);
  }

  findByTitle(title) {
    return http.get(`/todos?title=${title}`);
  }
}

export default new TodoDataService();

扩展阅读:《最好的 6 个免费天气 API 接口对比测评

创建 Vue 3 组件

接着我们来写 Vue Router 中定义的三个组件,也就是我们 app 页面上会显示的三个响应页。

「新增 ToDo 组件」 - AddTodo.vue

02-02-app-todo.jpg

上图为「AddTodo.vue」最终样式

这个组件用于向数据库中新增 ToDo 数据,它包含 2 个字段:titledescription

用它来调用咱们前一节写的 TodoDataService.create()

在 components 文件夹下创建 AddTodo.vue ,填入以下代码:

文件位置:vue-kalacloud-com/src/components/AddTodo.vue

<template>
  <div class="submit-form">
    <div v-if="!submitted">
      <div class="form-group">
        <label for="title">任务</label>
        <input
          type="text"
          class="form-control"
          id="title"
          required
          v-model="todo.title"
          name="title"
        />
      </div>

      <div class="form-group">
        <label for="description">详情</label>
        <input
          class="form-control"
          id="todo"
          required
          v-model="todo.description"
          name="description"
        />
      </div>

      <button @click="saveTodo" class="btn btn-success">提交</button>
    </div>

    <div v-else>
      <h4>提交成功</h4>
      <button class="btn btn-success" @click="newtodo">添加</button>
    </div>
  </div>
</template>

<script>
import TodoDataService from "../services/TodoDataService";

export default {
  name: "add-todo",
  data() {
    return {
      todo: {
        id: null,
        title: "",
        description: "",
        stauts: false
      },
      submitted: false
    };
  },
  methods: {
    savetodo() {
      var data = {
        title: this.todo.title,
        description: this.todo.description
      };

      TodoDataService.create(data)
        .then(response => {
          this.todo.id = response.data.id;
          console.log(response.data);
          this.submitted = true;
        })
        .catch(e => {
          console.log(e);
        });
    },
    
    newTodo() {
      this.submitted = false;
      this.todo = {};
    }
  }
};
</script>

<style>
.submit-form {
  max-width: 300px;
  margin: auto;
}
</style>

「ToDO 列表组件」 - TodoList.vue

02-01-app-todolist.jpg

上图为「TodoList.vue」最终效果

该组件调用 3 个 TodoDataService 方法:

  • getAll()
  • deleteAll()
  • findByTitle()

components 文件夹下创建 TodoList.vue,填入以下代码:

文件位置:vue-kalacloud-com/src/components/TodoList.vue

<template>
  <div class="list row">
    <div class="col-md-8">
      <div class="input-group mb-3">
        <input type="text" class="form-control" placeholder="搜索待办清单"
          v-model="title"/>
        <div class="input-group-append">
          <button class="btn btn-outline-secondary" type="button"
            @click="searchTitle"
          >
            Search
          </button>
        </div>
      </div>
    </div>
    <div class="col-md-6">
      <h4>待办清单</h4>
      <ul class="list-group">
        <li class="list-group-item"
          :class="{ active: index == currentIndex }"
          v-for="(todo, index) in todos"
          :key="index"
          @click="setActiveTodo(todo, index)"
        >
          {{ todo.title }}
        </li>
      </ul>

      <button class="m-3 btn btn-sm btn-danger" @click="removeAllTodos">
       删除所有清单
      </button>
    </div>
    <div class="col-md-6">
      <div v-if="currentTodo">
        <h4>待办详情</h4>
        <div>
          <label><strong>任务:</strong></label> {{ currentTodo.title }}
        </div>
        <div>
          <label><strong>详情:</strong></label> {{ currentTodo.description }}
        </div>
        <div>
          <label><strong>状态:</strong></label> {{ currentTodo.stauts ? "done" : "undone" }}
        </div>

        <router-link :to="'/todos/' + currentTodo.id" class="badge badge-warning">编辑</router-link>
      </div>
      <div v-else>
        <br />
        <p>点击右侧列表查看详情</p>
      </div>
    </div>
  </div>
</template>

<script>
import TodoDataService from "../services/TodoDataService";

export default {
  name: "todos-list",
  data() {
    return {
      todos: [],
      currentTodo: null,
      currentIndex: -1,
      title: ""
    };
  },
  methods: {
    retrieveTodos() {
      TodoDataService.getAll()
        .then(response => {
          this.todos = response.data;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },

    refreshList() {
      this.retrieveTodos();
      this.currentTodo = null;
      this.currentIndex = -1;
    },

    setActiveTodo(todo, index) {
      this.currentTodo = todo;
      this.currentIndex = todo ? index : -1;
    },

    removeAllTodos() {
      TodoDataService.deleteAll()
        .then(response => {
          console.log(response.data);
          this.refreshList();
        })
        .catch(e => {
          console.log(e);
        });
    },
    
    searchTitle() {
      TodoDataService.findByTitle(this.title)
        .then(response => {
          this.todos = response.data;
          this.setActiveTodo(null);
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    }
  },
  mounted() {
    this.retrieveTodos();
  }
};
</script>

<style>
.list {
  text-align: left;
  max-width: 750px;
  margin: auto;
}
</style>

「 ToDo 内容更新组件」 - Todo.vue

02-03-app-addtodo.jpg

上图为「Todo.vue」最终完成效果

components 文件夹下创建 Todo.vue,填入以下代码:

文件位置:vue-kalacloud-com/src/components/Todo.vue

<template>
  <div v-if="currentTodo" class="edit-form">
    <h4>待办事宜</h4>
    <form>
      <div class="form-group">
        <label for="title">任务:</label>
        <input type="text" class="form-control" id="title"
          v-model="currentTodo.title"
        />
      </div>
      <div class="form-group">
        <label for="description">详情:</label>
        <input type="text" class="form-control" id="description"
          v-model="currentTodo.description"
        />
      </div>

      <div class="form-group">
        <label><strong>状态:</strong></label>
        {{ currentTodo.stauts ? "done" : "undone" }}
      </div>
    </form>

    <button class="badge badge-primary mr-2"
      v-if="currentTodo.stauts"
      @click="updatestauts(false)"
    >
      未完成
    </button>
    <button v-else class="badge badge-primary mr-2"
      @click="updatestauts(true)"
    >
      完成
    </button>

    <button class="badge badge-danger mr-2"
      @click="deleteTodo"
    >
      删除
    </button>

    <button type="submit" class="badge badge-success"
      @click="updateTodo"
    >
      更新
    </button>
    <p>{{ message }}</p>
  </div>

  <div v-else>
    <br />
    <p>Please click on a Todo...</p>
  </div>
</template>

<script>
import TodoDataService from "../services/TodoDataService";

export default {
  name: "todo",
  data() {
    return {
      currentTodo: null,
      message: ''
    };
  },
  methods: {
    getTodo(id) {
      TodoDataService.get(id)
        .then(response => {
          this.currentTodo = response.data;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },

    updatestauts(status) {
      var data = {
        id: this.currentTodo.id,
        title: this.currentTodo.title,
        description: this.currentTodo.description,
        stauts: status
      };

      TodoDataService.update(this.currentTodo.id, data)
        .then(response => {
          console.log(response.data);
          this.currentTodo.stauts = status;
          this.message = '状态修改成功。';
        })
        .catch(e => {
          console.log(e);
        });
    },

    updateTodo() {
      TodoDataService.update(this.currentTodo.id, this.currentTodo)
        .then(response => {
          console.log(response.data);
          this.message = '信息更新成功';
        })
        .catch(e => {
          console.log(e);
        });
    },

    deleteTodo() {
      TodoDataService.delete(this.currentTodo.id)
        .then(response => {
          console.log(response.data);
          this.$router.push({ name: "todos" });
        })
        .catch(e => {
          console.log(e);
        });
    }
  },
  mounted() {
    this.message = '';
    this.getTodo(this.$route.params.id);
  }
};
</script>

<style>
.edit-form {
  max-width: 300px;
  margin: auto;
}
</style>

设置 Vue3 项目访问端口

在 Vue3 项目根目录创建 vue.config.js 文件,代码如下:

文件位置:vue-kalacloud-com/vue.config.js

module.exports = {
  devServer: {
    port: 8081
  }
}

我们已经设置了我们的应用程序在端口上运行8081

扩展阅读:《最好的 5 款翻译 API 接口对比测评

运行 Vue3 并在浏览器里查看效果

05-01-run-vue.jpg

至此,前端部分就全部完成了,我们来运行一下刚刚搭建的 Vue 前端代码,在浏览器里欣赏一下你自己的开发成果。

(1)在项目根目录执行:npm run serve

(2)如果没有报错,会看到 App running at 两段网址,一个是本地访问地址,一个是外网访问地址。

打开浏览器,输入http://localhost:8081/,在浏览器中我们可以看到前端页面已经显示出来了。

如果你也看到和下图类似的界面,这说明你的 Vue3 已经搭建完成,恭喜。

05-02-vue-web.jpg

因为我们的后端还没有搭建,前端界面还没有连接数据,所以只有轮廓没有数据。

下一章,我将带领大家一起搭建后端部分。

四. 后端部分 - node.js + Express + sequelize + MySQL

接下来,我们来一起配置后端部分,后端部分我们使用 node.js + Express + MySQL 的方式来构建。node.js 是一个开源跨平台运行环境,它让 JavaScript 可以运行在后端服务器上,Express 是 node.js Web app 框架,其底层是对 node.js 的 HTTP 模块封装,增加路由,中间件等特性,我们会在本教程中使用 Express 搭建 RESTful API ,让前后端通过 API 进行数据交换。最后是 MySQL 数据库,最终前后端操作的数据会存放在 MySQL 数据库中。

以下是 node.js Express 生成的 RESTful API

后端 node.js 项目结构

06-01-node.jpg

  • db.config.js 包含远程连接 MySQL 数据库的登录参数
  • server.js 包含 Express Web 服务器初始化配置
  • models/index.js 包含 MySQL 数据库的配置信息
  • models/todo.model.js : 包含 Sequelize 数据模型
  • controllers/todo.controller.js:包含所有增删改查操作的路由

好了,整体的后端服务器架构介绍就到这里,接下来进入实践环节,请打开你的 Terminal 我们一起来。切记,只有亲手实践过,才能透彻理解。

安装及配置 node.js

node.js 是一种 JavaScript 的运行环境,它可以让 JS 脱离浏览器在后端服务器上运行。本教程的后端环境使用 node.js 搭建。请先确认你的计算机中是否已安装 node.js 。如果尚未安装请前往 node 官网下载安装

安装或准备可远程连接的 MySQL 数据库

本教程搭建的 app 数据存放在 MySQL 中,你可以在本机安装 MySQL ,也可以准备一台可远程连接的 MySQL 数据库。

如果你还没有安装 MySQL 数据库,可根据《如何安装 MySQL》教程安装 MySQL 数据库,或在腾讯云之类的云服务商购买现成的 MySQL 数据库。

准备好 node.js 和 MySQL 数据库后,咱们就开始搭建后端部分。

创建 node.js App

在根目录创建 node.js 的项目文件夹

mkdir nodejs-mysql-kalacloud-com
cd nodejs-mysql-kalacloud-com

接下来配置后端的所有操作都在 nodejs-mysql-kalacloud-com 这个文件夹中完成。

我们先来初始化 node.js,使用 npm init 配置 package.json 文件。package.json 定义了当前项目所需要的各种模块以及项目配置信息(包含当前项目所需的开发和运行环境等信息)。

npm init

06-02-node-init.png

npm init
name: (nodejs-express-sequelize-mysql)
version: (1.0.0)
description: Node.js Rest Apis with Express, Sequelize , MySQL.
entry point: (index.js) server.js
test command:
git repository:
keywords: nodejs, express, sequelize, mysql, rest, api
author: kalacloud
license: (ISC)
Is this ok? (yes) yes

跟随 node.js 初始化程序填写相应的 app 初始化信息。

{
  "name": "nodejs-mysql-kalacloud-com",
  "version": "1.0.0",
  "description": "Node.js Rest Apis with Express, Sequelize , MySQL.",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "nodejs",
    "express",
    "sequelize",
    "mysql",
    "rest",
    "api"
  ],
  "author": "kalacloud",
  "license": "ISC"
}

初始化程序填写完成之后,npm 会自动帮你在根目录下生成package.json 配置文件。

接着我们来安装项目中需要用到的模块:expresssequelizemysql2body-parser

在项目根目录 nodejs-mysql-kalacloud-com 执行 npm 命令:

npm install express sequelize mysql2 body-parser cors --save

扩展阅读:《PAW 使用教程 - 手把手教你 API 接口测试

配置 Express Web 服务器

在根目录中,创建一个新的 server.js 文件

文件位置:nodejs-mysql-kalacloud-com/server.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();

var corsOptions = {
  origin: "*"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
  res.json({ message: "欢迎访问卡拉云 todolist 后端服务器" });
});

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`服务器运行端口: ${PORT}.`);
});
  • 我们导入了 expressbody-parsercors 模块
  • express 用于构建 Rest API 帮助前后端实现通讯。
  • body-parser 用于解析请求并创建 req.body 对象
  • cors 提供 Express 中间件
  • corsOptions 这里设置了可访问后端的前端来源为 * ,这意味着任何前端都可以接入此后端。这样设置并不安全,为了避免「跨域问题」,先这么设置。之后请在这里限制可访问的前端服务器。
  • 后端服务在 8080 端口上侦听指令

06-03-node-server.jpg

好,现在我们在根目录运行指令:node server.js 启动后端服务器。

06-04-node-chrome.jpg

在浏览器中输入 http://localhost:8080 ,你可以看到后端服务器欢迎信息,这表明我们的后端服务器已经启动。

使用 Sequelize 连接数据库

Sequelize 是一个基于 Promise 的 Node.js ORM,目前支持 Postgres、MySQL、SQLite 和 Microsoft SQL Server。 它是一个很成熟的框架,有很好的性能和速度。

我们先来配置 Sequelize ,在根目录新建 app 文件夹,然后再其中建一个 config 文件夹,我们把 Sequelize 的配置文件放在这里,新建 db.config.js 文件,在这个文件中写入你数据库连接的配置信息。

文件位置:nodejs-mysql-kalacloud-com/app/config/db.config.js

module.exports = {
  HOST: "192.168.1.5",
  USER: "kalacloud",
  PASSWORD: "YOUR_PASSWORD",
  DB: "vue",
    port:3306,
  dialect: "mysql",
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};
  • HOST 这里是你的 MySQL 服务器地址,如果数据库跑在本地就是 loaclhost,如果在腾讯云等云服务上,就填写云服务给你的数据库地址,比如 cdb-5nvdsixo.bj.tencentcdb.com 这是腾讯云的数据库地址样式。
  • USER 数据库登录用户名
  • PASSWORD 用户名对应的登录密码
  • DB 数据库名称
  • port 数据库远程访问端口
  • max 最大连接数
  • min 最小连接数
  • acquire 超时时间
  • idle 空闲时间

更多细节可访问 Sequelize 参数映射表 查看更多

初始化 Sequelize

我们将在 app / models 文件夹中初始化 Sequelize。

app/models 新建 index.js 文件并写入以下代码。

文件位置:nodejs-mysql-kalacloud-com/app/models/index.js

const dbConfig = require("../config/db.config.js");

const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
  host: dbConfig.HOST,
  dialect: dbConfig.dialect,
  operatorsAliases: false,

  pool: {
    max: dbConfig.pool.max,
    min: dbConfig.pool.min,
    acquire: dbConfig.pool.acquire,
    idle: dbConfig.pool.idle
  }
});

const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

db.todos = require("./todo.model.js")(sequelize, Sequelize);

module.exports = db;

然后在根目录下的 server.js 文件里添加 sync() 调用的方法:

文件位置:nodejs-mysql-kalacloud-com/server.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(bodyParser.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
  res.json({ message: "欢迎访问卡拉云 todolist" });
});

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`服务器运行端口: ${PORT}.`);
});

const db = require("./app/models");
db.sequelize.sync();

在结尾处添加这两行即可,你可以直接把 server.js 里的代码全删掉,然后复制上面的代码进去,保证你的项目代码与本教程完全一致。

定义 Sequelize Model

models 文件夹中,像这样创建 todo.model.js 文件

文件位置:nodejs-mysql-kalacloud-com/app/models/todo.model.js

module.exports = (sequelize, Sequelize) => {
  const Todo = sequelize.define("todo", {
    title: {
      type: Sequelize.STRING
    },
    description: {
      type: Sequelize.STRING
    },
    status: {
      type: Sequelize.BOOLEAN
    }
  });

  return Todo;
};

Sequelize Model 是向 MySQL 中指定数据库的写入列,这里会自动生成 ID,title,description,status,createdAt,updatedAt 这六个列。

初始化 Sequelize 之后,我们不需要在写任何增删改查函数,直接调就可以了。

  • 创建一个新清单:[create](https://sequelize.org/master/class/lib/model.js~Model.html#static-method-create)(object)
  • 通过 id 查找清单:[findByPk](https://sequelize.org/master/class/lib/model.js~Model.html#static-method-findByPk)(id)
  • 获取所有待办清单:[findAll](https://sequelize.org/master/class/lib/model.js~Model.html#static-method-findAll)()
  • 根据 ID 更新清单:[update](https://sequelize.org/master/class/lib/model.js~Model.html#static-method-update)(data, where: { id: id })
  • 根据 ID 删除清单:[destroy](https://sequelize.org/master/class/lib/model.js~Model.html#static-method-destroy)(where: { id: id })
  • 删除所有清单:destroy(where: {})
  • 在所有清单中按标题查找:findAll({ where: { title: ... } })

是不是超级方便,这些函数,我们会在接下来创建的「控制器」中使用。

扩展阅读:《API是什么: 一篇讲透API

创建控制器(controllers)

app/controllers 文件夹中,我们来创建一个控制器 todo.controller.js ,把上面 Sequelize 写入控制器来操作数据。

文件位置:nodejs-mysql-kalacloud-com/app/controllers/todo.controller.js

const db = require("../models");
const Todo = db.todos;
const Op = db.Sequelize.Op;

// Create and Save a new Todo
exports.create = (req, res) => {
  // Validate request
  if (!req.body.title) {
    res.status(400).send({
      message: "Content can not be empty!"
    });
    return;
  }

  // Create a Todo
  const todo = {
    title: req.body.title,
    description: req.body.description,
    stauts: req.body.stauts ? req.body.stauts : false
  };

  // Save Todo in the database
  Todo.create(todo)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while creating the Todo."
      });
    });
};

// Retrieve all Todos from the database.
exports.findAll = (req, res) => {
  const title = req.query.title;
  var condition = title ? { title: { [Op.like]: `%${title}%` } } : null;

  Todo.findAll({ where: condition })
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving todos."
      });
    });
};

// Find a single Todo with an id
exports.findOne = (req, res) => {
  const id = req.params.id;

  Todo.findByPk(id)
    .then(data => {
      if (data) {
        res.send(data);
      } else {
        res.status(404).send({
          message: `Cannot find Todo with id=${id}.`
        });
      }
    })
    .catch(err => {
      res.status(500).send({
        message: "Error retrieving Todo with id=" + id
      });
    });
};

// Update a Todo by the id in the request
exports.update = (req, res) => {
  const id = req.params.id;

  Todo.update(req.body, {
    where: { id: id }
  })
    .then(num => {
      if (num == 1) {
        res.send({
          message: "Todo was updated successfully."
        });
      } else {
        res.send({
          message: `Cannot update Todo with id=${id}. Maybe Todo was not found or req.body is empty!`
        });
      }
    })
    .catch(err => {
      res.status(500).send({
        message: "Error updating Todo with id=" + id
      });
    });
};

// Delete a Todo with the specified id in the request
exports.delete = (req, res) => {
  const id = req.params.id;

  Todo.destroy({
    where: { id: id }
  })
    .then(num => {
      if (num == 1) {
        res.send({
          message: "Todo was deleted successfully!"
        });
      } else {
        res.send({
          message: `Cannot delete Todo with id=${id}. Maybe Todo was not found!`
        });
      }
    })
    .catch(err => {
      res.status(500).send({
        message: "Could not delete Todo with id=" + id
      });
    });
};

// Delete all Todos from the database.
exports.deleteAll = (req, res) => {
  Todo.destroy({
    where: {},
    truncate: false
  })
    .then(nums => {
      res.send({ message: `${nums} Todos were deleted successfully!` });
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while removing all todos."
      });
    });
};

// Find all stauts Todos
exports.findAllstauts = (req, res) => {
  Todo.findAll({ where: { stauts: true } })
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving todos."
      });
    });
};

至此,整个后端部分就搭建完成了,我们把后端运行起来看看效果。

运行 Node.js Express 服务器

在 node.js 服务器根目录,运行 node server.js

06-04-node-all-server.jpg

控制台显示对数据库的读写,每当前端调用后端时,这里就会给出对应后端操作了写什么的 log

使用 Postman 调用 node.js 后端

06-06-postman.jpg

后端搭建起来后,我们可以使用 postman 来对它进行测试。

Vue3 + node.js 前后端一起运行

我们把前后端一起运行起来,然后打开浏览器看看最终的效果。

06-05-vue-nodejs-done.jpg

前端 Vue3 已经可以通过 node.js 连接到 MySQL 数据库中读取到对应的数据,整个项目搭建完成。

扩展阅读:《最好用的 5 款 React 富文本编辑器

五. Vue3 + node.js 前后端搭建「总结」

首先恭喜你完成 Vue3 + node.js 前后端搭建并成功运行,你离全栈工程师又近了一步。 Vue3 + node.js 相当实用,掌握好它们,任何你脑中想要实现的项目都可快速搭出来。

有没有更简单的方法来实现前后端的项目搭建呢?有。 推荐使用卡拉云,卡拉云帮你解决了前后端搭建的全部问题,你完全不用掌握任何前端后端技术,只需要用几行 SQL 代码把数据库 & API 接进去。再复杂的项目,也只需正常开发的 10% 的工时即可完成。

97-kalacloud-mianbaoduo.gif

卡拉云是一套低代码开发工具,仅需简单拖拽即可快速搭建后台工具。立即试用卡拉云,一分钟快速搭建属于你自己的后台工具。

六. 本文作者

wechat-jiangchuan.jpg

蒋川,卡拉云联合创始人,B 端数据开发。

如果你有任何问题,欢迎添加微信一起交流。我的微信 HiJiangChuan

扩展阅读: