在这个项目实战中,我们将构建一个待办事项管理Web应用。用户可以注册、登录、添加待办事项、修改待办事项状态、删除待办事项等。本文将详细介绍项目的基本架构和后端技术。
## 1. 项目简介
### 1.1 技术栈
以下是本项目实战所使用的技术栈:
- 后端:Node.js、Express
- 数据库:MongoDB
- 前端(下篇文章中):HTML、CSS、JavaScript、Bootstrap、jQuery
- 版本控制:Git
## 2. 项目准备
### 2.1 安装相关工具
确保你已经安装了以下工具:
### 2.2 创建项目文件夹
打开命令行工具,创建项目文件夹并进入:
mkdir todo-app
cd todo-app
### 2.3 初始化Git仓库
在项目文件夹中初始化Git仓库:
git init
创建.gitignore
文件,排除一些不需要跟踪的文件和文件夹:
node_modules/
*.log
## 3. 后端搭建
### 3.1 初始化项目
运行npm init
,按照提示填写信息,初始化项目。这将生成一个package.json
文件,用于管理项目依赖和配置。
### 3.2 安装依赖
安装后端所需的依赖:
npm install express mongoose bcryptjs jsonwebtoken
- express:Web应用框架
- mongoose:MongoDB对象模型工具
- bcryptjs:加密库
- jsonwebtoken:生成和验证JSON Web Token(JWT)
### 3.3 搭建服务器
创建一个名为server.js
的文件,并写入以下代码:
const express = require("express");
const app = express();
// 使用JSON中间件来解析请求体
app.use(express.json());
app.get("/", (req, res) => {
res.send("Hello World!");
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
运行node server.js
启动服务器,访问http://localhost:3000
,你应该看到"Hello World!"。
### 3.4 数据库连接
确保你已经安装并启动了MongoDB。在server.js
中添加以下代码以连接数据库:
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/todo_app", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("Connected to MongoDB"))
.catch((err) => console.error("Could not connect to MongoDB", err));
现在,我们已经成功连接到了MongoDB数据库。接下来,我们将创建用户认证和待办事项相关的接口。
### 3.5 用户认证
首先,我们要创建用户模型。在项目根目录下创建一个名为models
的文件夹。然后,在models
文件夹中创建一个名为user.js
的文件,并添加以下代码:
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
// 在保存用户之前,对密码进行哈希处理
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
// 验证密码的实例方法
userSchema.methods.validatePassword = function (password) {
return bcrypt.compare(password, this.password);
};
const User = mongoose.model("User", userSchema);
module.exports = User;
接下来,我们将创建注册和登录接口。在项目根目录下创建一个名为routes
的文件夹。然后,在routes
文件夹中创建一个名为auth.js
的文件,并添加以下代码:
const express = require("express");
const router = express.Router();
const jwt = require("jsonwebtoken");
const User = require("../models/user");
router.post("/register", async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).send({ message: "User registered successfully" });
} catch (err) {
res.status(400).send({ error: err.message });
}
});
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
if (!user || !(await user.validatePassword(req.body.password))) {
throw new Error("Invalid username or password");
}
const token = jwt.sign({ userId: user._id }, "your_jwt_secret");
res.send({ token });
} catch (err) {
res.status(401).send({ error: err.message });
}
});
module.exports = router;
最后,在server.js
中添加以下代码来挂载认证路由:
const authRoutes = require("./routes/auth");
// ...
app.use("/auth", authRoutes);
// ...
现在,我们已经完成了用户认证相关的接口。可以使用Postman等API测试工具测试注册和登录接口。
### 3.6 待办事项接口
首先,我们要创建待办事项模型。在models
文件夹中创建一个名为todo.js
的文件,并添加以下代码:
const mongoose = require("mongoose");
const todoSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
title: {
type: String,
required: true,
},
completed: {
type: Boolean,
default: false,
},
});
const Todo = mongoose.model("Todo", todoSchema);
module.exports = Todo;
接下来,我们将创建待办事项相关的接口。在routes
文件夹中创建一个名为todos.js
的文件,并添加以下代码:
const express = require("express");
const router = express.Router();
const Todo = require("../models/todo");
router.get("/", async (req, res) => {
try {
const todos = await Todo.find({});
res.send(todos);
} catch (err) {
res.status(500).send({ error: err.message });
}
});
router.post("/", async (req, res) => {
try {
const todo = new Todo(req.body);
await todo.save();
res.status(201).send(todo);
} catch (err) {
res.status(400).send({ error: err.message });
}
});
router.put("/:id", async (req, res) => {
try {
const todo = await Todo.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true,
});
if (!todo) throw new Error("Todo not found");
res.send(todo);
} catch (err) {
res.status(400).send({ error: err.message });
}
});
router.delete("/:id", async (req, res) => {
try {
const todo = await Todo.findByIdAndDelete(req.params.id);
if (!todo) throw new Error("Todo not found");
res.send({ message: "Todo deleted successfully" });
} catch (err) {
res.status(400).send({ error: err.message });
}
});
module.exports = router;
最后,在server.js
中添加以下代码来挂载待办事项路由:
const todoRoutes = require("./routes/todos");
// ...
app.use("/todos", todoRoutes);
// ...
至此,我们已经完成了待办事项接口的开发。可以使用Postman等API测试工具测试待办事项相关接口。
## 4. 启动Web应用
在完成上篇文章中的步骤后,我们已经搭建好了后端服务器。现在我们需要启动后端服务器:
- 确保已经安装并运行了MongoDB。
- 打开命令行(终端),进入项目文件夹
todo-app
。
cd todo-app
```
- 运行
server.js
文件以启动服务器:
node server.js
```
如果一切正常,你应该看到以下输出:
````sh
Server running on port 3000
Connected to MongoDB
```
现在,后端服务器已经在端口3000上运行,并与MongoDB数据库连接。接下来,我们将构建前端部分。
## 5. 前端搭建
### 5.1 创建前端文件夹和文件
在项目根目录下创建一个名为public
的文件夹,用于存放前端文件。接下来,在public
文件夹中创建以下文件:
index.html
:主页面style.css
:样式表script.js
:JavaScript脚本
### 5.2 编写前端页面
编辑index.html
文件,添加以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo App</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1 class="text-center mt-5">Todo App</h1>
<!-- 在这里添加表单和待办事项列表 -->
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="script.js"></script>
</body>
</html>
5.3 添加表单和待办事项列表
在index.html
文件的<!-- 在这里添加表单和待办事项列表 -->
位置添加以下代码:
<form class="mt-4" id="todo-form">
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" id="title" placeholder="Enter todo title">
</div>
<button type="submit" class="btn btn-primary">Add Todo</button>
</form>
<div class="mt-4">
<h3 class="mb-3">Todo List</h3>
<ul class="list-group" id="todo-list">
<!-- 在这里显示待办事项 -->
</ul>
</div>
5.4 编写CSS样式
编辑style.css
文件,添加以下代码:
body {
font-family: Arial, sans-serif;
}
.completed {
text-decoration: line-through;
}
5.5 编写JavaScript脚本
编辑script.js
文件,添加以下代码:
$(document).ready(function () {
// 获取待办事项列表
function getTodos() {
// 在这里编写获取待办事项列表的代码
}
// 添加待办事项
function addTodo() {
// 在这里编写添加待办事项的代码
}
// 修改待办事项状态
function toggleTodo() {
// 在这里编写修改待办事项状态的代码
}
// 删除待办事项
function deleteTodo() {
// 在这里编写删除待办事项的代码
}
// 事件绑定
$("#todo-form").on("submit", function (event) {
event.preventDefault();
addTodo();
});
$("#todo-list").on("click", ".toggle-todo", function () {
toggleTodo();
});
$("#todo-list").on("click", ".delete-todo", function () {
deleteTodo();
});
// 初始化
getTodos();
});
``在`script.js`中,我们已经创建了基本的函数框架。接下来,我们需要实现这些函数,以便与后端服务器进行交互。
### 5.6 实现获取待办事项列表函数
在`getTodos`函数中添加以下代码:
```javascript
$.get("/api/todos", function (data) {
$("#todo-list").empty();
data.forEach(function (todo) {
var listItem = $("<li class='list-group-item d-flex justify-content-between align-items-center'></li>");
var title = $("<span class='todo-title'></span>").text(todo.title);
if (todo.completed) {
title.addClass("completed");
}
var buttonGroup = $("<div class='btn-group'></div>");
var toggleButton = $("<button class='btn btn-sm btn-outline-secondary toggle-todo'></button>").text(todo.completed ? "Undo" : "Complete");
var deleteButton = $("<button class='btn btn-sm btn-outline-danger delete-todo'></button>").text("Delete");
buttonGroup.append(toggleButton, deleteButton);
listItem.append(title, buttonGroup);
listItem.data("id", todo._id);
listItem.data("completed", todo.completed);
$("#todo-list").append(listItem);
});
});
5.7 实现添加待办事项函数
在addTodo
函数中添加以下代码:
var title = $("#title").val().trim();
if (title) {
$.post("/api/todos", { title: title }, function () {
$("#title").val("");
getTodos();
});
}
5.8 实现修改待办事项状态函数
在toggleTodo
函数中添加以下代码:
var listItem = $(this).closest("li");
var id = listItem.data("id");
var completed = listItem.data("completed");
$.ajax({
url: "/api/todos/" + id,
type: "PUT",
data: { completed: !completed },
success: function () {
getTodos();
}
});
5.9 实现删除待办事项函数
在deleteTodo
函数中添加以下代码:
var listItem = $(this).closest("li");
var id = listItem.data("id");
$.ajax({
url: "/api/todos/" + id,
type: "DELETE",
success: function () {
getTodos();
}
});
至此,我们已经完成了前端页面的构建。现在,你可以运行后端服务器,然后在浏览器中访问http://localhost:3000
来查看并使用这个待办事项应用。
为了使应用在 Express 服务器中正确运行,请确保在 server.js
文件中添加以下代码:
app.use(express.static("public"));
确保将其添加到其他中间件之前,例如:
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const app = express();
const PORT = process.env.PORT || 3000;
mongoose.connect("mongodb://localhost/todo_app", { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.set("useFindAndModify", false);
app.use(cors());
app.use(express.json());
app.use(express.static("public")); // 将此行添加到此处
app.use("/api/todos", require("./routes/todos"));
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
其他内容我们下篇再继续 可以订阅本专栏 有更新 第一时间推送给你。