🚀 快捷跳转
在学会了怎么使用Express框架之后,就可以使用该框架进行项目开发
🔅 项目结尾有完整项目代码
本章要开发的项目是许愿墙,许愿墙最初是一种建筑,用于承载人们的愿望,人们可以在上面涂涂画画或贴上小纸片,在上面写下自己的愿望、期盼和祝福等。现今许愿墙也用于网络上,一般是网站独立的一个空间页面,供人们进行许愿、祈祷和祝福等
1. 需求分析
为了增加网站人气,市场部门要设置一个活动吸引用户参与,提出了许愿墙的想法,产品经理根据市场部门的需求,规划了一个许愿墙产品
前端开发人员根据UI设计图已经开发完毕,实现的页面效果如下图所示
🔔 小提示
请忽略页面整体美观性,我们要实现的是整体功能的跑通🥲
产品需求如下:
- 展示最近10条用户的许愿信息
- 用户的许愿信息使用便笺的形式粘贴在页面上
- 便笺要用随机颜色,并摆放在页面的任意位置
- 用户可以拖动便笺和关闭便笺
- 用户可以通过提交表单的方式将自己的许愿信息添加进去
- 添加许愿信息的时候要进行表单验证,禁止提交空的姓名或空的许愿内容
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. 系统流程图
根据解决方案,可以得到以下的系统流程图
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)
(3)将表单提交过来的处理数据插入MySQL数据库中,然后返回成功页面,如下图所示
好了,分析完前端页面之后,下面就是重头戏实战开发了;下面先从创建MySQL数据库表开始
4. 创建MySQL数据库表
要创建MySQL数据库表,首先需要安装MySQL,请自行「掘金」搜索进行安装;安装完并启动之后,使用数据库可视化工具Navicat来创建数据库表;关于Navicat的下载安装方法请自行「掘金」搜索进行安装
4-1. 创建数据库wish
(1)打开Navicat工具,单击「连接」按钮,选择「MySQL」,弹出「新建连接」对话框,如下图所示;在连接名文本框中输入「本机」,接着输入本地MySQL的主机名、端口、用户名和密码,然后单击「连接测试」按钮,即可连接本地MySQL
(2)连接到本地数据库后右击本地数据库连接,在弹出的快捷菜单中选择「新建数据库」命令,如下图所示
(3)在弹出的「新建数据库」对话框中输入数据库名「wish」,字符集选择「utf8mb4」,排序规则选择「utf8mb4_0900_bin」,单击「确定」按钮,如下图所示
创建数据库成功后,就可以在本地数据库连接列表中看到刚刚创建的wish数据库了
4-2. 创建数据表wish
数据库创建成功后,接下来创建数据表
(1)双击wish数据库将其打开,然后右击wish数据库下的「表」,弹出快捷菜单,如下图所示
(2)在弹出的菜单中选择「新建表」命令打开新建表窗口,如下图所示,在打开的新建表的窗口中新增5个字段,其中需要注意的是,id字段要设置成自动递增
wish表各字段及其作用如下:
| 字段名 | 类型 | 作用 | | --- | --- | --- | | id | int | 数据表主键 | | name | varchar | 许愿姓名 | | content | varchar | 许愿内容 | | created_at | datetime | 创建时间 | | updated_at | datetime | 更新时间 |
添加完毕后单击「保存」按钮,输入数据表名wish,即可成功保存
4-3. 添加模拟数据
为了便于之后的列表展示查看效果,在前端没有提交表单添加数据的情况下,需要在数据库表中添加一些模拟数据
这里添加了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. 新增路由
本项目里需要新增下面两个路由:
- 首页路由,即用户打开许愿墙首页,后端接收数据处理的路由
- 提交表单处理路由,即用户添加愿望提交,后端接收数据处理的路由
更改项目里默认的路由文件,需要修改项目根目录下 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文件 ,将路由的方法放在其中,以避免由于页面路由太多而导致查看不便的问题
将路由真正的处理方法放在根目录下的 controllers 目录中,并将针对首页的路由处理方法放在 controllers 目录中的 index.js 文件中
5-6. 新增 constant(常量)
为了便于管理返回值,在项目的根目录下创建一个 constant 目录,用来存放项目中会用到的常量
在 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 文件
代码如下:
// 默认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 文件
用来存放 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
在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