WechatIMG44.jpeg


🚀 快捷跳转

前端全栈框架 - Express 安装与使用

前端全栈框架 - Express 路由中间件详解


在学会了怎么使用Express框架之后,就可以使用该框架进行项目开发

🔅 项目结尾有完整项目代码

本章要开发的项目是许愿墙,许愿墙最初是一种建筑,用于承载人们的愿望,人们可以在上面涂涂画画或贴上小纸片,在上面写下自己的愿望、期盼和祝福等。现今许愿墙也用于网络上,一般是网站独立的一个空间页面,供人们进行许愿、祈祷和祝福等

1. 需求分析

为了增加网站人气,市场部门要设置一个活动吸引用户参与,提出了许愿墙的想法,产品经理根据市场部门的需求,规划了一个许愿墙产品

前端开发人员根据UI设计图已经开发完毕,实现的页面效果如下图所示

image.png


🔔 小提示

请忽略页面整体美观性,我们要实现的是整体功能的跑通🥲


产品需求如下:

  1. 展示最近10条用户的许愿信息
  2. 用户的许愿信息使用便笺的形式粘贴在页面上
  3. 便笺要用随机颜色,并摆放在页面的任意位置
  4. 用户可以拖动便笺和关闭便笺
  5. 用户可以通过提交表单的方式将自己的许愿信息添加进去
  6. 添加许愿信息的时候要进行表单验证,禁止提交空的姓名或空的许愿内容

2. 系统设计

在整个系统设计的过程中,通过和前端开发人员讨论,决定了最终的实现方案:使用后端渲染方式,就是前端开发人员将页面写好,即完成页面结构、样式和一些拖曳效果,然后将页面交给后端开发人员,后端开发人员将数据处理好,通过模板引擎将数据以变量的方式展示到页面模板上,最后输出页面到浏览器渲染

2-1. 实现目标和解决方案

根据产品需求,确定实现目标为产品需求中的(1)、(5)、(6)项

下面针对以上实现目标进行分析

(1)展示最近10条用户的许愿信息

展示最近10条用户的许愿信息就是需要拿出来最近10条的用户许愿信息,然后交给前端页面渲染出来

在这里使用MySQL数据库将用户的许愿信息保存在该数据库中的一张表中,然后需要从这张表中取出数据,要求是最近的10条信息,这个可以直接通过MySQL数据库特定的SQL语句来实现

(2)用户可以通过提交表单的方式将自己的许愿信息添加进去

用户提交表单也就是将数据提交到后端,后端要处理数据,然后将它存入数据库中

这里后端要做两件事情:第一件是接收前端提交的表单数据;第二件是处理数据,通过SQL语句将数据保存在指定的MySQL数据库的数据表中

(3)添加许愿信息的时候要进行表单验证,禁止提交空的姓名或空的许愿内容

添加信息时要验证,禁止提交空信息的这个功能点其实和上一个功能点有些相似,就是在接收到前端提交过来的表单数据时,要进行相应的验证,不允许有空值出现;如出现空值则返回错误信息,如没有空值则继续执行,将接收的表单数据插入MySQL数据库中

由此,得出针对实现目标的解决方案如下:

  • (1)利用SQL语句从MySQL数据库中取出最近的10条数据后返回前端
  • (2)接收到表单数据并插入到MySQL数据库中
  • (3)接收到表单数据进行判断验证,如果是空值则返回错误,如果是非空值则继续执行后续逻辑

2-2. 系统流程图

根据解决方案,可以得到以下的系统流程图

image.png

2-3. 开发环境

该项目所使用的开发环境及软件版本如下表所示

| 名称 | 版本 | | --- | --- | | 操作系统 | Mac 12.5 | | Node.js 版本 | 14.13.0 | | Express 版本 | 4.16.0 | | art-template 版本 | 4.13.2 | | mySQL 版本 | 8.0.31 | | 浏览器 | EDGE | | 开发工具 | vsCode |

3. 前端页面分析

结合前面的解决方案和页面效果图分析可知:

(1)将从MySQL数据库中查询得到的最近10条记录以列表的形式返回页面,页面通过列表渲染,得到10个代表愿望的便笺,其位置和颜色随机

(2)单击「提交」按钮提交表单到后端,接收数据之后,判断姓名和愿望是否为空;如果为空,则返回错误页面,如下图所示;如果不为空,则继续执行后续逻辑(3)

image.png

(3)将表单提交过来的处理数据插入MySQL数据库中,然后返回成功页面,如下图所示

image.png

好了,分析完前端页面之后,下面就是重头戏实战开发了;下面先从创建MySQL数据库表开始

4. 创建MySQL数据库表

要创建MySQL数据库表,首先需要安装MySQL,请自行「掘金」搜索进行安装;安装完并启动之后,使用数据库可视化工具Navicat来创建数据库表;关于Navicat的下载安装方法请自行「掘金」搜索进行安装

4-1. 创建数据库wish

(1)打开Navicat工具,单击「连接」按钮,选择「MySQL」,弹出「新建连接」对话框,如下图所示;在连接名文本框中输入「本机」,接着输入本地MySQL的主机名、端口、用户名和密码,然后单击「连接测试」按钮,即可连接本地MySQL

image.png

(2)连接到本地数据库后右击本地数据库连接,在弹出的快捷菜单中选择「新建数据库」命令,如下图所示

image.png

(3)在弹出的「新建数据库」对话框中输入数据库名「wish」,字符集选择「utf8mb4」,排序规则选择「utf8mb4_0900_bin」,单击「确定」按钮,如下图所示

image.png

创建数据库成功后,就可以在本地数据库连接列表中看到刚刚创建的wish数据库了

4-2. 创建数据表wish

数据库创建成功后,接下来创建数据表

(1)双击wish数据库将其打开,然后右击wish数据库下的「表」,弹出快捷菜单,如下图所示

image.png

(2)在弹出的菜单中选择「新建表」命令打开新建表窗口,如下图所示,在打开的新建表的窗口中新增5个字段,其中需要注意的是,id字段要设置成自动递增

image.png

wish表各字段及其作用如下:

| 字段名 | 类型 | 作用 | | --- | --- | --- | | id | int | 数据表主键 | | name | varchar | 许愿姓名 | | content | varchar | 许愿内容 | | created_at | datetime | 创建时间 | | updated_at | datetime | 更新时间 |

添加完毕后单击「保存」按钮,输入数据表名wish,即可成功保存

4-3. 添加模拟数据

为了便于之后的列表展示查看效果,在前端没有提交表单添加数据的情况下,需要在数据库表中添加一些模拟数据

image.png

这里添加了10条模拟数据便于演示,等项目完成后可以根据需要删除这些模拟数据

5. 创建项目

首先,根据 第一章 的学习内容,使用Express框架创建一个项目,在命令行中输入以下命令,在目录中生成一个名为wish的Express项目

express wish

5-1. 安装依赖包

首先如同所有的项目一样,先执行npm install命令安装项目需要的基础依赖包;另外,针对此项目引用下表中的5个依赖包

| 依赖包名 | 作用 | | --- | --- | | art-template | 模版引擎 | | express-art-template | 模版引擎 | | async | 异步处理方法库 | | MySQL2 | MySQL数据库支持 | | sequelize | 操作MySQL的ORM框架 |

分别执行以下命令安装这5个依赖包:

npm install art-template -S
npm install express-art-template -S
npm install async -S
npm install mysql2-S
npm install sequelize -S

5-2. 更改默认端口

由于Express创建项目后默认端口为3000,为了方便演示和避免与其他项目冲突,将端口号改为3001;更改方法是修改项目根目录下bin目录中的 www.js 文件,将其中的代码

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

更改为

var port = normalizePort(process.env.PORT || '3001');
app.set('port', port);

使用 npm start 命令启动项目,在浏览器中打开 http://localhost:3001 ,即可访问项目的首页

5-3. 更换模板引擎

由于Express框架默认的渲染模板引擎是jade,所以需要将它更换成 art-template 模板引擎,它具有优秀的渲染性能和简洁的语法

更换的方法很简单,打开项目根目录下的 app.js 文件,找到下面这行代码:

app.set('view engine', 'jade');

将它替换成下面这段代码:

app.engine('html', require('express-art-template'));
app.set('view engine', 'html');

这样就已经替换成功了;现在可以将项目根目录下的页面目录views中的文件全部删除,重新加入一些HTML文件

5-4. 新增路由

本项目里需要新增下面两个路由:

  1. 首页路由,即用户打开许愿墙首页,后端接收数据处理的路由
  2. 提交表单处理路由,即用户添加愿望提交,后端接收数据处理的路由

更改项目里默认的路由文件,需要修改项目根目录下 routes 目录中的 index.js 文件为以下代码:

// 引入Express对象
var express = require ('express');           
// 引入路由对象
var router = express.Router ();              
// 引入自定义的controller
const IndexController = require('../controllers/index');
// 定义首页路由
router.get ('/', IndexController.getList); 
// 定义提交表单路由
router.post ('/add', IndexController.add); 
// 导出路由,供app.js文件调用
module.exports = router; 

5-5. 新增 controller(处理方法)

在项目根目录下创建一个 controllers 目录,然后在目录中建立 index.js文件 ,将路由的方法放在其中,以避免由于页面路由太多而导致查看不便的问题

image.png

将路由真正的处理方法放在根目录下的 controllers 目录中,并将针对首页的路由处理方法放在 controllers 目录中的 index.js 文件中

5-6. 新增 constant(常量)

为了便于管理返回值,在项目的根目录下创建一个 constant 目录,用来存放项目中会用到的常量

image.png

在 constant 目录下新建一个 constant.js 文件,新增如下代码:

// 定义一个对象
const obj = {
  // 默认请求成功
  DEFAULT_SUCCESS: {
    code: 10000,
    msg: ''
  },
  // 默认请求失败
  DEFAULT_ERROR: {
    code: 188,
    msg: '出现错误'
  },
  // 定义错误返回-缺少必要参数
  LACK: {
    code: 199,
    msg: '缺少必要参数'
  }
};
// 导出对象,给其他方法调用
module.exports = obj;

5-7. 新增配置文件

为了便于更换数据库域名等信息,需要将数据库的连接信息放在一个专门存放配置信息的文件中,在项目的根目录下创建一个 config.js 文件

image.png

代码如下:

// 默认dev配置
const config = {
  // 是否调试模式
  DEBUG: true,
  // MySQL数据库配置
  MYSQL: {
    host: 'localhost',
    database: 'wish',
    username: 'root',
    password: 'hyc123789'
  }
};

if (process.env.NODE_ENV === 'production') {
  // 生产环境MySQL数据库配置
  config.MYSQL = {
    host: 'aaa.mysql.rds.aliyuncs.com',
    database: 'aaa',
    username: 'aaa',
    password: 'aaa'
  };
}
// 导出配置
module.exports = config;

默认使用的是开发环境的MySQL连接配置;当环境变成生产环境的时候,再使用生产环境的MySQL连接配置

5-8. 新增数据库配置文件

为了便于其他文件引用数据库对象,将数据库对象实例化放在了一个单独的文件里;在根目录下新建一个 db.js 文件

image.png

用来存放 Sequelize 的实例化对象,代码如下:

// 引入Sequelize模块
var Sequelize = require('sequelize');
// 引入数据库连接配置
var CONFIG = require('./config');

// 实例化数据库对象
var sequelize = new Sequelize(CONFIG.MYSQL.database, CONFIG.MYSQL.username, CONFIG.MYSQL.password, {
  host: CONFIG.MYSQL.host,
  // 数据库类型
  dialect: 'mysql',

  // 是否打印日志
  logging: CONFIG.DEBUG ? console.log : false,

  // 配置数据库连接池
  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },

  // 时区设置
  timezone: '+08:00'
});
// 导出实例化数据库对象
module.exports = sequelize;

5-9. 新增model文件(数据库映射文件)

在安装完数据库支持并增加了数据库配置之后,还需要定义model,用来实现数据库表的映射;在项目的根目录下新建一个models目录,用来存放model文件,在里面新建一个wish.js文件,用来对应创建的MySQL数据表wish

image.png

在wish.js文件中定义了一个model,代码如下:

// 引入Sequelize模块
const Sequelize = require('sequelize');

// 引入数据库实例
const db = require('../db');

// 定义model
const Wish = db.define('Wish', {
  // 主键
  id: {type: Sequelize.INTEGER, primaryKey: true, allowNull: false, autoIncrement: true},
  // 许愿姓名
  name: {type: Sequelize.STRING(20), allowNull: false},
  // 许愿内容
  content: {type: Sequelize.STRING, allowNull: false}
}, {
  // 是否支持驼峰
  underscored: true,
  // MySQL数据库表名
  tableName: 'wish',
});
// 导出model
module.exports = Wish;

6. 渲染许愿列表

在定义完成项目的一些基本配置之后,就可以将前端写好的页面放入项目中进行页面的渲染

首先将前端提供的HTML文件放入项目根目录下的views目录中,并命名为index.html,然后将CSS文件和JS文件分别放入项目根目录下的public目录中的CSS目录和JS目录,修改HTML文件代码的引用路径

接着修改HTML文件代码,增加art-template模板引擎代码,将前端的静态列表更换成使用art-template渲染的动态列表。实际的HTML代码如下:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>许愿墙</title>
  <link rel="stylesheet" href="/css/index.css">
</head>
<body>
<h2 class="title" style="">快来写上您的愿望吧</h2>
<!--js动态填入数据,使用一个data-list属性,将列表传入到页面上-->
<div id="container" data-list="{{list}}"></div>
<form action="add" method="post" id="form" style="height: 136px;">
  <input type="text" class="input" id="name" name="name" placeholder="说出你的大名">
  <input id="content" class="input" type="text" placeholder="许个愿吧" name="content" />
  <button class="submit" type="submit">提交</button>
</form>
<script src="/js/jquery-3.1.1.min.js"></script>
<script src="/js/index.js"></script>
</body>
</html>

由于页面上的愿望便笺是JS动态渲染出来的,所以若要将数据传给JS,则需要先将数据渲染到页面上#container元素中的一个data-list属性上,然后用JS获取后再进行渲染;相关的JS代码如下:

var container;
// 颜色
var colors = ['#207de0', '#42baec', '#e3e197', '#6cde47', '#ecc733'];
//创建许愿便笺
var createItem = function (name, content) {
    var color = colors[parseInt (Math.random () * 5)];
    $ ('<div class="item"><p>' + name + ':</p><p>' + content + '</p><a
href="#">关闭</a></div>').css ({'background': color}).appendTo (container).
drag ();
  };
  var list = container.attr ('data-list'); // 获取元素container的属性data-list
  // 循环遍历list,创建便笺
  $.each (JSON.parse(list), function (i, v) {
    createItem (v.name, v.content);
  });

现在只需要将数据列表通过list变量渲染到页面上即可,修改项目根目录下controllers目录中的index.js文件,也就是IndexController文件

首先来看一下IndexController文件的代码主结构:

const Common = require('./common');                   // 引入共用方法
const async = require('async');                        // 引入async
const WishModel = require('../models/wish');        // 引入wish表的model
const Constant = require('../constant/constant');  // 引入常量constant
// 配置导出对象
let exportObj = {
  getList,
  add
};
module.exports = exportObj;                        // 导出对象,供路由文件调用
// 获取许愿列表的方法
function getList(){
  // 获取许愿列表的逻辑
}
// 添加许愿方法
function add(){
  // 添加许愿逻辑
}

文件中引入了一些必要的依赖,声明了两个方法,接下来编写获取许愿列表的方法

根据前面指定的解决方案,后端接收到浏览器请求,需要从MySQL数据库中按照创建的时间倒序查询出最近10条许愿记录;代码如下:

// 获取许愿列表方法
function getList (req, res) {
  // 定义一个async任务
  let tasks = {
    // 执行查询方法
    query: cb => {
      // 使用Sequelize的model的findAll方法查询
      WishModel
      .findAll({
        limit: 10,
        order: [['created_at', 'DESC']],
      })
      .then(function (result) {
          // 查询结果处理
          let list = [];                // 定义一个空数组list,用来存放最终结果
          // 遍历SQL查询出来的结果,处理后装入list
          result.forEach((v, i) => {
            let obj = {
              id: v.id,
              name: v.name,
              content: v.content
            };
            list.push(obj);
          });
          cb(null, list);     // 通过async提供的回调,返回数据到下一个async方法
        })
        .catch(function (err) {
          // 错误处理
          console.log(err);  // 打印错误日志
          // 通过async提供的回调,返回数据到下一个async方法
          cb(Constant.DEFAULT_ERROR);
        });
    }
  };
  // 让async自动控制流程
  async.auto(tasks, function (err, result) {
    if(err){
      console.log (err)      // 如果错误存在,则打印错误
    }else{
      // 如果没有错误,则渲染index页面模板,同时将之前query方法获取的结果数组list
          以变量list渲染到页面上
      res.render ('index', {
        list: result['query']
      });
    }
  })
}

将以上查询代码插入到之前的getList方法中,接着在浏览器中打开 http://localhost:3001 进行查看,发现已经能够正常访问;只是一开始是前端的模拟数据,现在是数据库中实时存在的数据,当然数据库中的数据也是前期模拟填进去的

到这里就完成了从MySQL数据库中查询数据返回页面、渲染列表等一系列动作;下一节开始讲解如何在前端提交表单即可添加愿望的处理操作

7. 添加许愿处理

用户在许愿页面添加许愿即提交表单,将数据发送到后端,后端处理之后,通常会有一个返回,这里使用一个页面告知用户提交的结果

在项目根目录下的页面目录views中新建一个result.html文件,由于主要是给大家演示后端的功能,所以前端只是简单地写了一个页面,而并没有写CSS代码和JS代码;对应的result.html代码如下:

<! DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{result}}</title>
</head>
<body>
<! --提示信息-->
<h2>{{msg}}</h2>
<! --返回按钮-->
<h2><a href="/">返回</a></h2>
</body>
</html>

在result.html中增加了两个渲染变量result和msg,需要后端渲染到页面上,这会在之后的controller方法中体现

下面来看一下添加许愿表单对应的HTML代码

<! -- 添加许愿表单 -->
<form action="add" method="post" id="form" style="height: 136px; ">
  <! -- 许愿者姓名 -->
  <input type="text" class="input" id="name" name="name" placeholder=
"说出你的大名">
  <! -- 许愿内容 -->
  <input id="content" class="input" type="text" placeholder="许个愿吧"
name="content" />
  <! -- 提交按钮 -->
  <button class="submit" type="submit">提交</button>
</form>

使用POST请求,后端接收的路由为“/add”,对应的controller方法为IndexController中的add方法;对应的add方法的代码如下:

// 添加愿望处理方法
function add (req, res) {
  // 定义一个async任务
  let tasks = {
    // 验证必填参数方法
    checkParams: cb => {
      Common.checkParams(req.body, ['name', 'content'], cb)
    },
    // 执行添加方法
    add: ['checkParams', (results, cb) => {
      // 使用Sequelize的model的create方法插入
      WishModel
      .create({
        name: req.body.name,
        content: req.body.content
      })
        .then(function (result) {
          cb(null);                           // 插入结果成功处理
        })
        .catch(function (err) {
          // 错误处理
          console.log(err);                 // 打印错误日志
          // 通过async提供的回调,返回数据到下一个async方法
          cb(Constant.DEFAULT_ERROR);
        });
    }]
  };
  // 让async自动控制流程
  async.auto(tasks, function (err, result) {
    if(err){
      // 错误处理
      console.log (err);                    // 打印错误日志
      let result = ’失败’;
      let msg = ’添加失败,出现错误’;
      if(err.code === 199){
        // 199代表参数缺少错误,和在constant.js文件中定义的对应
        msg = ’添加失败,姓名和愿望都要填上哦’;
      }
      // 渲染失败结果的页面,将result和msg渲染到页面上
      res.render ('result', {
        result: result,
        msg: msg
      });
    }else{
      // 渲染成功结果的页面,将result和msg渲染到页面上
      res.render ('result', {
        result: ’成功!',
        msg: ’添加成功,返回去看一下’
      });
    }
  })
}

将以上代码插入到IndexController的add方法中,接着在浏览器中打开 http://localhost:3001 ,在表单中随意添加文本,然后单击「提交」按钮

修改输入的数据,继续单击「提交」按钮,出现成功提示后单击返回按钮,返回到首页,会看到刚刚添加的数据已经呈现在了页面上

至此,许愿墙项目开发结束

项目代码 : 提取码: pgnn