一. 需求分析
1 系统框架
选择前后端分离的系统架构

2 具体的业务需求
- 有一个页面, 可以展示用户的列表
- 有一个页面, 可以添加用户信息
- 有一个页面, 可以修改用户信息
- 点击删除按钮, 可以删除用户信息

二. 技术方案
1 数据库设计
用户表 (users)
| 字段名 | 类型 | 备注 |
|---|---|---|
| id | int | 主键, 自增 |
| name | varchar(255) | 用户的姓名 |
| age | tinyint unsigned | 用户的年龄 |
使用Navicate建表
2 接口设计
1) baseURL
http://localhost:3000
2) 用户模块
获取所有用户
请求
请求方式: GET
请求URL: /users
GET /users
响应
[
{
"id": 1,
"name": "xiaoming",
"age": 20
},
{
"id": 2,
"name": "xiaomei",
"age": 18
},
]
根据id获取单个用户
请求
请求方式 GET
请求地址 /users/:id
GET /users/:id
GET /users/1
响应
{
"id": 1,
"name": "xiaoming",
"age": 20
}
添加用户
请求
请求方式 POST
接口地址 /users
POST /users
请求参数
{
"name": "xiaoming",
"age": 20
}
响应
{
"id": 1,
"name": "xiaoming",
"age": 20
}
修改用户
请求
请求方式 PUT
请求地址 /users/1
PUT /users/:id
PUT /users/1
请求参数
{
"name": "xiaoming-new",
"age": 200
}
响应
{
"id": 1,
"name": "xiaoming-new",
"age": 200
}
删除用户
请求
请求方式 DELETE
请求地址 /users/:id
DELETE /users/:id
DELETE /users/1
响应
空
三. 技术实现
1 后端
1) 项目搭建
安装express脚手架
执行命令, 全局安装脚本架(也称为项目生成器)
npm i express-generator -g
安装完成后, 在全局会多一个命令
express
通过脚手加搭建项目
在根目录下, 执行命令
express --no-view backEnd
- 在目录下创建一个
backEnd的目录, 作为后端项目的目录 --no-view: 创建一个数据服务, 不提供页面服务
安装相关依赖
进入backEnd目录, 执行命令, 根据package.jsoon中的依赖项, 安装项目所有的依赖
- cookie-parser: 解析cookie的中间件
- debug: 调试代码的中间件
- express: express框架包
- morgan: 记录日志的中间件
npm install
启动项目
使用nodemon启动项目,
将nodemon作为开发时依赖安装
npm i nodemon -D
会在package.json中, 生成devDependencies
"devDependencies": {
"nodemon": "^2.0.15"
}
修改pagckage.json中的脚本
"scripts": {
"start": "nodemon ./bin/www"
},
执行
npm run start
使用浏览器测试

2) 实现获取所有用户
编写router/users.js文件
router.get('/', (req, res) => {
// 一. 编写sql语句
// 二. 执行sql语句, 获得结果
// 三. 返回数据
})
安装mysql包
npm i mysql
复用数据库操作模块

修改配置
导入db模块
示例
const { getAll, getById, exec } = require('../db')
// 编写接口
// GET /users
router.get('/', async (req, res) => {
// 一. 编写sql语句
let sql = 'select * from users'
// 二. 执行sql语句, 获得结果
const users = await getAll(sql)
// 三. 返回数据
res.send(users)
})
测试
创建test/users.http测试文件
@baseURL = http://localhost:3000
###
GET {{baseURL}}/users

3) 实现根据id获取单个用户
// 根据id获取单个用户
// GET /users/:id -> {}
router.get('/:id', async (req, res) => {
// 一. 解析参数
let { id } = req.params
// 二. 操作数据库
let sql = `select * from users where id=${id}`
const user = await getById(sql)
// 三. 返回结果
res.send(user)
})
测试
4) 实现添加用户
// 添加用户
// POST /users {"name":"test", "age":20}
router.post('/', async (req, res) => {
// 一. 解析参数
const { name, age } = req.body
// 二. 操作数据库
let sql = `insert into users (name, age) values ('${name}', '${age}')`
// console.log(sql)
const resData = await exec(sql)
//console.log(resData)
// 三. 返回结果
res.send({
id: resData.insertId,
name, // name: name
age, // age: age
})
})
测试
5) 实现修改接口
// 修改用户
// PUT /users/:id {"name":"test-new", "age":30}
router.put('/:id', async (req, res) => {
// 一. 解析参数
let { id } = req.params
let { name, age } = req.body
// 二. 操作数据库
let sql = `update users set name='${name}', age='${age}' where id=${id}`
const resData = await exec(sql)
// 三. 返回结果
console.log(resData)
res.send({
id,
name,
age,
})
})
测试
6) 实现删除接口
// 删除用户
router.delete('/:id', async (req, res) => {
// 一. 解析参数
let { id } = req.params
// 二. 操作数据库
let sql = `delete from users where id = '${id}'`
await exec(sql)
// 三. 返回结果
res.status(204).send('')
})
测试
2 前端
前后端交互流程图
点击查看【processon】
1) 创建index.html
编写页面结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户列表</title>
</head>
<body>
<h1>用户列表</h1>
<div class="container">
<a href="./add.html" class="add-btn">添加用户</a>
<table class="user-list">
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>操作</th>
</tr>
</table>
</div>
</body>
</html>
编写样式
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
table,
th,
td {
border: 1px solid #000;
}
.container {
width: 80%;
padding: 20px 50px;
}
.container .add-btn {
/* 第一段: position display */
display: inline-block;
/* 第二段: 盒子模型 margin padding border width height */
padding: 12px 20px;
border: 1px solid #169bd5;
/* 第三段: 背景相关 */
background-color: #ecf5ff;
/* 第四段: 文本相关 */
color: #169bd5;
line-height: 1;
text-align: center;
cursor: pointer;
/* 第五段: CSS3新增属性 */
border-radius: 5px;
box-sizing: border-box;
transition: all 0.2s;
}
.container .add-btn:hover {
background-color: #fff;
color: #169bd5;
}
.container .user-list {
margin-top: 20px;
width: 500px;
border: 1px solid #000;
border-spacing: 0px;
border-collapse: collapse;
text-align: center;
}
.form-item {
margin: 20px auto;
}
.form-item input {
padding: 0 15px;
border: 1px solid #dcdfe6;
height: 40px;
color: #606266;
outline: none;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
border-radius: 4px;
}
.form-item input:focus {
border-color: #409eff;
}
编写js
引入jquery, 使用npm下载. 使用离线版文件
<script src="./lib/jquery.min.js"></script>
发送ajax请求
// 调用接口(获取所有用户)
$.ajax({
url: 'http://localhost:3000/users',
type: 'GET',
success: function (res) {
console.log(res)
},
})
发现报错:
原因
发生了跨域请求
跨域请求
- 由于浏览器同源策略导致跨域问题
- 当浏览器从一个域向另一个域发送请求时, 它认为这种请求不安全, 不允许发送
域 = 协议 + 域名 + 端口
最常用的解决方案
在服务端开启CORS, 在服务端回复的响应头中, 加入特殊的头信息, 允许浏览器发送跨域请求
在后端安装cors中间件
https://www.npmjs.com/package/cors
npm i cors
在app.js中导入cors中间件
const cors = require('cors')
全局注册cors()中间件
app.use(cors())
改造前台
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户列表</title>
<link rel="stylesheet" href="./css/reset.css" />
<link rel="stylesheet" href="./css/index.css" />
<script src="./lib/jquery.min.js"></script>
</head>
<body>
<h1>用户列表</h1>
<div class="container">
<a href="./add.html" class="add-btn">添加用户</a>
<table class="user-list">
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>操作</th>
</tr>
</table>
</div>
<script src="./js/index.js"></script>
</body>
</html>
// 通过jquery发送ajax请求. 调用接口, 获取数据
$.ajax({
url: 'http://localhost:3000/users',
type: 'GET',
success: function (res) {
// 发送请求, 获取的数据存放在res里
console.log(res)
// 遍历res数组
res.forEach((item) => {
// 渲染table表格
let str = `<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.age}</td>
<td>
<a href="./edit.html">修改</a>
<a href="#">删除</a>
</td>
</tr>`
$('.user-list').append(str)
})
},
})
2) 创建add.html
编写页面结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>添加用户</title>
<link rel="stylesheet" href="./css/reset.css" />
<link rel="stylesheet" href="./css/index.css" />
</head>
<body>
<h1>添加用户</h1>
<div class="container">
<div class="form-item">
用户名:
<input type="text" id="name" name="name" placeholder="请输入用户名" />
</div>
<div class="form-item">
年龄:
<input type="text" id="age" name="age" placeholder="请输入年龄" />
</div>
<button id="btn" class="add-btn">添加</button>
</div>
</body>
</html>
编写样式
.form-item {
margin: 20px auto;
}
.form-item input {
padding-left: 15px;
border: 1px solid #dcdfe6;
height: 40px;
color: #606266;
outline: none;
cursor: pointer;
border-radius: 4px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.form-item input:focus {
border-color: #409eff;
}
编写js
3) 创建edit.html
修改入口
$.ajax({
url: 'http://localhost:3000/users',
type: 'GET',
success: function (res) {
// console.log(res)
// 遍历res数组
// 创建一个tr元素, 追加到表格的最后
res.forEach((item) => {
const tr = `<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.age}</td>
<td>
<a href="./edit.html?id=${item.id}">修改</a>
<a href="#" onclick="handleDel(${item.id})">删除</a>
</td>
</tr>`
$('.user-list').append(tr)
})
},
})
编写页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>修改用户</title>
<!-- 二. 编写样式 -->
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/list.css" />
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
</head>
<body>
<!-- 一. 编写HTML结构 -->
<h1>修改用户</h1>
<div class="container">
<div class="form-item">
用户名:
<input
type="text"
id="username"
name="username"
placeholder="请输入用户名"
/>
</div>
<div class="form-item">
年龄:
<input type="text" id="age" name="age" placeholder="请输入年龄" />
</div>
<button id="btn" class="add-btn">提交</button>
</div>
<script>
// 三. 发送ajax请求, 根据id查找用户的信息
// console.log(window.location)
// console.log(window.location.search)
// console.log(window.location.search.replace('?', ''))
// console.log(window.location.search.replace('?', '').split('='))
// console.log(window.location.search.replace('?', '').split('=')[1])
const id = window.location.search.replace('?', '').split('=')[1]
$.ajax({
type: 'GET',
url: 'http://localhost:3000/users/' + id,
success: function (res) {
console.log(res)
// 设置input的值
$('#username').val(res.name)
$('#age').val(res.age)
},
})
// 绑定btn的click事件
$('#btn').click(function () {
// 获取input新输入的值
const name = $('#username').val()
const age = $('#age').val()
if (name == '' || age == '') {
alert('用户名密码不能为空')
return
}
// 发送put请求
$.ajax({
type: 'PUT',
url: 'http://localhost:3000/users/' + id,
data: { name, age },
success: function (res) {
alert('更新成功')
window.location.href = './index.html'
},
})
})
</script>
</body>
</html>
4) 实现删除
// 处理删除
function handleDel(id) {
// 通过id参数得到 要删除的 数据的id
$.ajax({
type: 'DELETE',
url: 'http://localhost:3000/users/' + id,
success: function (res) {
window.location.href = './index.html'
},
})
}
