## 1. 项目简介
### 1.1 技术栈
以下是本项目实战所使用的技术栈:
- 后端:Node.js、Express
- 数据库:MongoDB
- 前端(下篇文章中):HTML、CSS、JavaScript、Bootstrap、jQuery
- 版本控制:Git
## 2. 项目准备
### 2.1 安装相关工具
确保你已经安装了以下工具:
1. [Node.js](https://nodejs.org/)
2. [npm](https://www.npmjs.com/)(Node.js包管理工具,通常与Node.js一起安装)
3. [Git](https://git-scm.com/)
4. [MongoDB](https://www.mongodb.com/)
### 2.2 创建项目文件夹
打开命令行工具,创建项目文件夹并进入:
1. mkdir todo-app 2. 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`中添加以下代码来挂载待办事项路由:
1. const todoRoutes = require("./routes/todos"); 2. 3. // ... 4. 5. app.use("/todos", todoRoutes); 6. 7. // ...
至此,我们已经完成了待办事项接口的开发。可以使用Postman等API测试工具测试待办事项相关接口。
## 4. 启动Web应用
在完成上篇文章中的步骤后,我们已经搭建好了后端服务器。现在我们需要启动后端服务器:
1. 确保已经安装并运行了MongoDB。
2. 打开命令行(终端),进入项目文件夹`todo-app`。
1. cd todo-app 2. ```
3. 运行`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}`); });
其他内容我们下篇再继续 可以订阅本专栏 有更新 第一时间推送给你。