普通视图

发现新文章,点击刷新页面。
昨天以前首页

入坑node.js全面指南(三)

作者 温暖前端
2025年6月30日 21:15

前两篇文章详细讲解了node.js的知识点,本篇文章来实现一个经典的全栈待办事项(Todo List)应用,采用前后端分离架构。后端基于 Node.js + Express + TypeScript,前端基于 Vue 3 + TypeScript,数据持久化采用 MongoDB。项目结构清晰,适合学习和实践全栈开发。

项目源码地址链接: todolist-express: 一个基于 Vue3 + TypeScript + Node.js + Express + MongoDB 的全栈 ToDoList 项目。

项目截图

image.png

image.png

image.png

一、后端技术栈与实现

1. 技术选型

  • Node.js:高性能的 JavaScript 运行环境,适合 I/O 密集型应用。
  • Express:简洁灵活的 Web 框架,便于快速搭建 RESTful API。
  • TypeScript:为 JavaScript 提供类型系统,提升代码可维护性和可读性。
  • Mongoose:MongoDB 的对象建模工具,简化数据库操作。

2. 目录结构

backend/
  ├── src/
  │   ├── app.ts         // Express 应用主入口
  │   ├── index.ts       // 启动服务
  │   ├── models/        // Mongoose 数据模型
  │   └── routes/        // 路由模块

3. 主要知识点

a) TypeScript 在后端的应用

  • 类型定义提升了开发效率,减少了运行时错误。
  • 接口(interface)用于定义数据结构,如 Todo、User。
import mongoose, { Schema, Document } from 'mongoose';

export interface ITodo extends Document {
  content: string;
  status: 'active' | 'completed' | 'deleted';
  priority: number;
  tags: string[];
  category: string;
  user: mongoose.Types.ObjectId;
  createdAt: Date;
  completedAt?: Date;
}

b) 路由与中间件

  • 路由模块化,routes/auth.ts 处理认证相关接口,routes/todo.ts 处理待办事项相关接口。
  • 使用中间件进行请求体解析、鉴权等操作。
// 获取所有 ToDo
router.get('/', auth, async (req: AuthRequest, res: Response) => {
  const todos = await Todo.find({ user: req.user.id, status: { $ne: 'deleted' } }).sort({ createdAt: -1 });
  res.json(todos);
});

c) Mongoose 数据建模

  • models/Todo.tsmodels/User.ts 定义了数据结构和约束。
  • 通过 Schema 约束字段类型、必填项、默认值等。

d) 用户认证

  • 通常采用 JWT(JSON Web Token)进行用户身份验证(可在 auth.ts 路由中实现)。
  • 注意密码加密存储(如 bcrypt),避免明文存储。
// 鉴权中间件
function auth(req: AuthRequest, res: Response, next: NextFunction) {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).json({ msg: '未登录' });
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch {
    res.status(401).json({ msg: '无效token' });
  }
}

e) 错误处理

  • 统一的错误处理机制,保证接口返回一致的错误格式,便于前端处理。

  • Vue 3:响应式、组件化的前端框架。

  • TypeScript:提升代码可维护性。

  • Vite:新一代前端构建工具,极快的热更新体验。

  • Pinia/Vuex:状态管理。

二、前端技术栈与实现

2. 目录结构

frontend/
  ├── src/
  │   ├── api/           // 封装后端请求
  │   ├── components/    // 复用组件
  │   ├── store/         // 状态管理
  │   ├── views/         // 页面视图
  │   └── router/        // 路由配置

3. 主要知识点

a) 组件化开发

  • 复用性强,易于维护,如 TodoItem.vueTodoList.vue
  • 父子组件通信(props、emit)、兄弟组件通信(状态管理)。

b) 状态管理

  • store/todo.tsstore/user.ts 管理全局状态,避免多层组件传参。
  • 响应式数据驱动视图更新。

c) 路由管理

  • router/index.ts 配置页面路由,实现页面跳转与权限控制。

d) API 封装

  • api/request.ts 封装 axios/fetch,统一处理请求和响应。
  • api/todo.tsapi/user.ts 封装具体业务接口,便于维护。

e) 样式与动画

  • assets/animate.css 提供动画效果,提升用户体验。

三、项目运行以及MongoDB启动指南

项目运行

  1. 安装 Node.js、MongoDB
  2. 分别进入 backend、frontend 目录执行 npm install
  3. 启动 MongoDB 服务
  4. 启动后端:npm run dev
  5. 启动前端:npm run dev
  6. 访问前端页面(如 http://localhost:5173)

MongoDB启动指南

  • 访问 MongoDB 官方下载页面
  • 选择 Windows 平台,版本建议选择最新稳定版(如 6.0)
  • 下载 MSI 安装程序(64 位)

在mongodb官网安装mongodb后,启动命令行输入mongod,仍旧显示 image.png

打开电脑的高级系统设置,配置mongodb的环境变量,将mongodb的bin目录添加到环境变量中

image.pngimage.png

启动mongodb服务的时候提示

image.png

可以通过在mongod/bin目录下cmd,输入mongod --dbpath D:\app\MongoDB\data启动数据库服务

数据库启动后,可以通过mongodb compass进行数据库连接

image.png

数据库启动并连接成功后,再运行服务端代码,服务端代码运行端如下图所示,则代表服务端代码成功运行

image.png

入坑node.js全面指南(二)

作者 温暖前端
2025年6月30日 08:21

1. Express.js 架构哲学

作为 Node.js 生态中应用最广泛的 Web 框架,Express.js 以"最小化设计原则"为核心,通过模块化架构实现灵活扩展。

核心设计理念

  • 简约而不简单:仅提供 Web 开发的基础功能,其他能力通过中间件扩展
  • 中间件驱动架构:采用洋葱模型实现请求处理流水线
  • 无强制约束:不限定项目结构,开发者拥有完全自由
  • 渐进式增强:可根据需求添加功能模块,避免过度设计

关键架构组件

  • 中间件系统:请求处理流水线,每个中间件可访问请求/响应对象
  • 路由分发器:基于 HTTP 方法和 URL 的高效路由匹配
  • 视图引擎接口:支持多种模板引擎(EJS、Pug等)
  • 扩展接口:通过 app.set()/app.get() 管理应用配置

安装

npm install express

基本应用结构

const express = require('express');
const app = express();
const port = 3000;

// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 路由
app.get('/', (req, res) => {
  res.send('Hello Express!');
});

// 启动服务器
app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

2. 中间件

中间件类型

  1. 应用级中间件:绑定到 app 实例

    // 记录请求时间的应用级中间件
    app.use((req, res, next) => {
      req.requestTime = Date.now();
      console.log(`请求时间: ${req.requestTime}`);
      next(); // 必须调用next传递控制权
    });
    
  2. 路由级中间件:绑定到路由实例

    const router = express.Router();
    router.use((req, res, next) => {
      console.log('路由中间件执行');
      next();
    });
    
  3. 错误处理中间件:四个参数的特殊中间件

    app.use((err, req, res, next) => {
      console.error(err.stack);
      res.status(500).send('服务器错误!');
    });
    
  4. 内置中间件

    app.use(express.json()); // 解析JSON请求体
    app.use(express.static('public')); // 静态文件服务
    
  5. 第三方中间件

    const helmet = require('helmet');
    app.use(helmet()); // 安全头部设置
    

中间件执行顺序

Express 中间件按照声明顺序执行,理解这一点对于正确配置应用至关重要:

  1. 请求到达服务器
  2. 按顺序执行匹配的中间件
  3. 每个中间件可以修改 req/res 对象
  4. 中间件通过 next() 传递控制权
  5. 当某个中间件发送响应时,链终止

3. 路由系统深度解析

路由基础

// 基础路由
app.get('/products', (req, res) => {
  res.json([{ id: 1, name: '手机' }]);
});

// 路由参数
app.get('/products/:id', (req, res) => {
  const id = req.params.id;
  res.json({ id, name: `商品${id}` });
});

// 查询参数
app.get('/search', (req, res) => {
  const q = req.query.q;
  res.send(`搜索: ${q}`);
});

4. 请求与响应处理

请求对象 (req)

  • req.params:路由参数
  • req.query:查询字符串参数
  • req.body:请求体内容(需要中间件解析)
  • req.cookies:客户端 cookies
  • req.headers:HTTP 请求头
  • req.ip:客户端 IP 地址

响应对象 (res)

  • res.status(code):设置状态码
  • res.send(body):发送响应
  • res.json(obj):发送 JSON 响应
  • res.render(view, locals):渲染模板
  • res.redirect(path):重定向
  • res.set(field, value):设置响应头
// 综合使用示例
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  
  if (!username || !password) {
    return res.status(400).json({ error: '用户名和密码必填' });
  }
  
  // 认证逻辑...
  res.cookie('token', 'abc123', { httpOnly: true });
  res.redirect('/dashboard');
});

5. Express vs Koa vs Node 深度对比

5.1 Express 与 Node.js 的关系

关系解析

  • Node.js 是运行时环境,提供 HTTP 模块等基础能力
  • Express 是基于 Node.js HTTP 模块的封装框架
  • Express 简化了路由、中间件等常见 Web 开发任务

5.2 Express 与 Koa 的深度对比

  • 设计理念:Express.js 设计较为灵活,提供了丰富的功能和插件,适合快速开发各种类型的 Web 应用。而 Koa 是由 Express.js 的原班人马打造,它更注重简洁和优雅,采用了更现代化的异步编程方式(如 async/await),并且没有捆绑任何中间件,开发者可以根据需求自由选择和组合中间件。

  • 中间件机制:Express.js 的中间件采用传统的回调函数形式,中间件之间通过next()方法传递控制权。在处理复杂的异步操作时,可能会出现回调地狱的问题。Koa 的中间件基于 async/await 语法,通过await next()实现中间件的执行和传递,代码更加简洁、易读,避免了回调地狱。

  • 错误处理:在 Express.js 中,错误处理通常通过在中间件或路由处理函数中捕获错误,并传递给错误处理中间件。而 Koa 使用 try/catch 块或者在 async 函数中返回错误,通过onerror事件来统一处理错误,错误处理更加直观和方便。

Express vs Koa 核心差异

特性 Express Koa
中间件模型 线性链式调用 洋葱模型
异步处理 回调函数 原生Async/Await
错误处理 集中式错误中间件 Try/Catch捕获
路由系统 内置Router 需koa-router
请求/响应封装 原生对象扩展 统一Context对象
体积大小 约4.5MB 约0.6MB

6. Express 实战案例:RESTful API 实现

const express = require('express');
const app = express();
app.use(express.json());

// 内存数据库
let users = [
  { id: 1, name: '张三', email: 'zhang@example.com' },
  { id: 2, name: '李四', email: 'li@example.com' }
];

// 获取所有用户
app.get('/api/users', (req, res) => {
  res.json({
    status: 'success',
    count: users.length,
    data: users
  });
});

// 创建新用户
app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  const newUser = {
    id: users.length + 1,
    name,
    email
  };
  
  users.push(newUser);
  
  res.status(201).json({
    status: 'success',
    data: newUser
  });
});

});

// 全局404处理
app.use((req, res) => {
  res.status(404).json({
    status: 'error',
    message: '路由不存在'
  });
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    status: 'error',
    message: '服务器内部错误'
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

7. 总结

Express.js的优势:

  1. 简洁核心+丰富扩展:保持核心轻量,通过中间件提供无限扩展
  2. 渐进式采用路径:从简单路由到复杂应用均可胜任
  3. 社区驱动生态:庞大的中间件生态系统解决各种场景需求
  4. 文档与稳定性:优秀的文档质量和稳定的API设计

最佳实践建议

  1. 使用 express.Router 实现路由模块化
  2. 中间件按功能拆分为独立模块
  3. 统一错误处理机制
  4. 使用 helmet 增强安全性
  5. 结合 morgan 记录访问日志
  6. 使用 express-validator 进行输入验证
❌
❌