尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Node.js+Express+MongoDB博客系统全栈开发实战指南

Node.js+Express+MongoDB博客系统全栈开发实战指南 简介在Web开发中Node.js以事件驱动和非阻塞I/O模型成为构建高并发I/O密集型应用的首选Express作为其生态中最成熟的轻量级框架通过灵活的中间件机制简化了路由与请求处理而MongoDB文档型数据库的JSON式存储方式恰好与JavaScript对象无缝衔接让数据建模更贴近业务逻辑。三者组合为博客管理系统这类内容型应用提供了高效、可扩展的技术底座。本文从环境配置、npm脚本权限排查等易踩坑环节入手深入讲解Mongoose Schema设计、JWT身份认证、RESTful API规范、聚合管道统计等核心知识点并结合权限控制、数据校验与索引优化给出完整的工程实践方案。无论是初学者完成第一个全栈项目还是开发者快速构建内容管理后台都能从中获得从理论到落地的系统性参考最终自然收敛到Node.jsExpressMongoDB博客系统的全流程搭建方法。 拿到一个NodejsExpressMongoDB实现博客管理系统.zip这样的压缩包很多人的第一反应是解压、装依赖、跑起来结果卡在node_modules安装失败、MongoDB 连不上、npm.ps1直接报权限错误折腾一晚上连项目长什么样都没看到。作为一个折腾过不少 Node.js 全栈项目的开发者我这次把整个博客管理系统的搭建过程完整梳理了一遍从环境配置到数据库建模从接口设计到鉴权逻辑全都记录在这里保证每一步能复现、能落地。这个项目适合谁看如果你是刚学完 Node.js 基础、想动手做第一个完整项目的新人或者在公司需要快速搭一个内容管理后台的开发者这篇文章可以帮你直接把Node.js Express MongoDB这条技术栈打通。读完你不仅会搭博客系统还能理解每个环节为什么要这么设计遇到了坑也知道怎么排查。1. 项目拆解博客管理系统到底在管什么1.1 核心需求梳理很多人拿到博客管理系统这个需求就直接开写接口这是最大的坑。先别急着敲代码我们把需求拆开看看一个能自洽运转的博客系统至少包含这么几块内容。第一块是用户体系。博客不可能让所有人都能发文章所以必须有注册、登录、身份校验至少区分管理员和普通访客。管理员能写文章、删评论普通访客只能浏览和发表评论。这个权限模型在你设计数据库的时候就会影响集合结构不能等写完了再补。第二块是文章管理。这是系统的中心模块包括文章的发布、编辑、删除、列表展示、详情查看还有分类和标签。分类的作用是让文章有归属感不至于堆成一团标签则更灵活一篇文章可以打多个标签。我在做这个项目的时候把分类做成了独立集合标签则直接存数组原因是分类数量少且稳定标签数量多且随意混在一起管理反而麻烦。第三块是评论管理。评论看着简单其实很容易被忽略。你需要考虑一篇文章下面挂多条评论一条评论归属某个用户同时评论还要支持删除管理员或评论作者本人。这部分如果设计不好后面做联表查询时会非常痛苦。第四块是统计与辅助功能比如文章总数、评论总数、分类文章数。这些数据不一定要做复杂报表但在管理后台首页展示几个数字卡片是很常见的需求MongoDB 的聚合管道在这里能派上大用场。1.2 技术选型为什么是 Node.js Express MongoDB这个技术组合不是随便选的每个成员都有自己的定位。Node.js 负责跑 JavaScript 服务端代码它的优势是事件驱动、非阻塞 I/O。博客系统是典型的 I/O 密集型应用——读文章、写评论、查用户几乎全是数据库操作没有太多 CPU 密集计算所以 Node.js 的模型非常匹配。更重要的是如果前端也用 JavaScript你就能实现前后端语言统一不需要在 JavaScript 和 PHP/Java 之间反复切换上下文。Express 是 Node.js 生态里最老牌、最稳定的 Web 框架。可能有人问现在不都流行 Nest.js 或者 Koa 吗为什么选 Express原因很实在Express 足够轻、中间件生态完善、社区资料最多新手遇到问题能搜到大量解决方案。对于博客系统这种规模的项目Express 完全够用而且它的路由和中间件机制能让你清楚理解 HTTP 请求处理的每一个环节这比直接上重型框架对你成长更有利。MongoDB 是目前最流行的文档型 NoSQL 数据库。博客系统的数据结构天然适合文档模型——一篇文章的正文、摘要、标签、作者信息可以完整存进一个文档不需要像关系型数据库那样拆成多张表再 JOIN。而且 MongoDB 的 JSON 格式和 JavaScript 对象无缝衔接在 Node.js 里操作几乎零阻抗。再加上 mongodb 官方提供的 Compass 可视化工具调试数据非常直观。1.3 项目目录结构设计项目结构直接影响你的代码能不能持续扩展。我见过有的同学把全部接口写在app.js里文件长到两千行后面改一个功能要翻半天。这个项目我推荐用 MVC 思想做分层但不用太复杂按职责拆成下面这个样子blog-system/ ├── app.js // 应用入口初始化 express 和中间件 ├── config/ │ └── db.js // 数据库连接配置 ├── models/ // 数据模型层 │ ├── User.js │ ├── Post.js │ ├── Comment.js │ └── Category.js ├── routes/ // 路由层 │ ├── auth.js // 注册、登录 │ ├── post.js // 文章增删改查 │ ├── comment.js // 评论相关 │ └── category.js // 分类管理 ├── controllers/ // 控制器层业务逻辑入口 │ ├── authController.js │ ├── postController.js │ ├── commentController.js │ └── categoryController.js ├── middlewares/ │ ├── auth.js // JWT 鉴权中间件 │ └── errorHandler.js // 全局错误处理 ├── utils/ │ └── response.js // 统一响应格式 └── package.json这个结构的核心逻辑是路由层只负责把 URL 映射到具体的控制器方法控制器处理业务逻辑模型层负责数据库操作。这样每一层只做一件事调试的时候能快速定位问题出在哪个环节。2. 环境准备Node.js 安装与 npm 常见坑2.1 Node.js 安装和环境变量配置很多新手卡在第一步其实 Node.js 的安装非常简单关键是理解安装之后发生了什么事。去 Node.js 官网下载 LTS 版本长期支持版别追新。安装包是.msi格式双击一路 Next 到完成。安装完成后你需要确认两件事Node.js 命令能不能用、环境变量有没有配好。打开终端Windows 下用 PowerShell 或 CMD输入node -v npm -v如果两个命令都能输出版本号说明安装成功。如果提示node 不是内部或外部命令那就是环境变量没配上。安装包通常会自动加但如果你下载的是绿色版或者手动改了路径就需要自己到系统属性 - 环境变量 - Path里把 Node.js 的安装目录比如C:\Program Files\nodejs\加进去。这里我想多说一句环境变量的作用系统执行命令的时候会在 Path 里面列出的所有目录里逐个找同名程序。你把 Node.js 安装目录加进 Path系统才能在任何位置调用node和npm。理解了这个原理以后配置其他工具比如 JDK、Python都是同一个套路。2.2 npm 脚本执行权限问题排查这是一个出现频率极高的坑我自己也踩过报错内容长这样npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。这个问题的原因不是 npm 坏了而是 PowerShell 的执行策略默认限制了.ps1脚本运行。npm 在 PowerShell 里不是直接作为程序执行而是通过npm.ps1这个脚本包装了一层执行策略拦住了它。解决办法是临时放开当前用户的脚本执行权限。以管理员身份打开 PowerShell执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser选Y确认即可。RemoteSigned的意思是本地创建的脚本可以运行从网络下载的脚本需要签名这是一个安全性和便利性平衡的合理选择。设置完重新打开终端npm 命令就能正常使用了。这段经验为什么值得写出来因为新手遇到这个报错很容易误判是 Node.js 没装好然后卸载重装一遍问题还是没解决白白浪费时间。2.3 安装 Express、Mongoose 和 MongoDB环境细节准备好之后开始装项目依赖。建议先建一个空目录然后在目录里初始化项目mkdir blog-system cd blog-system npm init -ynpm init -y会生成一个默认的package.json后面可以手动修改。接下来安装核心依赖npm install express mongoose bcryptjs jsonwebtoken cors dotenv这里简单介绍每个包的作用express是 Web 框架mongoose是 MongoDB 的 ODM对象文档映射bcryptjs用来加密密码jsonwebtoken生成和验证 JWT登录凭证cors解决跨域问题dotenv用来读取配置文件。再装一个开发依赖nodemon它可以在代码修改后自动重启服务不用每次手动重启npm install -D nodemon在package.json里配置启动脚本scripts: { dev: nodemon app.js, start: node app.js }至于 MongoDB 本身如果你是 Windows 用户直接去官网下载安装包一路下一步。安装完服务通常会自动启动。然后用 Compass 可视化工具连接mongodb://localhost:27017看到admin、local、config三个默认库就说明连接正常。3. 核心实战Express 服务搭建与路由设计3.1 Express 应用骨架与启动流程现在开始写真正的代码。先创建app.js这是整个应用的入口// app.js const express require(express); const cors require(cors); const dotenv require(dotenv); const connectDB require(./config/db); dotenv.config(); connectDB(); const app express(); // 中间件 app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 路由挂载 app.use(/api/auth, require(./routes/auth)); app.use(/api/posts, require(./routes/post)); app.use(/api/comments, require(./routes/comment)); app.use(/api/categories, require(./routes/category)); // 健康检查 app.get(/health, (req, res) { res.json({ status: ok }); }); const PORT process.env.PORT || 3000; app.listen(PORT, () { console.log(Server running on port ${PORT}); });这里我做了几个关键决策解释一下理由。express.json()是必需的内置中间件它能把请求体里的 JSON 数据解析出来挂到req.body上。没有它你通过 POST 提交的 JSON 数据在服务端全是undefined。express.urlencoded()则是用来解析表单格式的请求体前端表单提交时需要用到。cors()必须放在路由之前注册因为跨域请求会先经过预检OPTIONS 请求如果中间件还没注册预检直接失败浏览器控制台会报跨域错误。3.2 路由设计与 RESTful API 规范路由是整个接口层的门面设计得好不好直接影响前后端联调效率。这个项目我按照 RESTful 规范来设计接口核心思路是资源用名词表示操作用 HTTP 方法区分。用户相关接口方法路径功能权限POST/api/auth/register用户注册公开POST/api/auth/login用户登录公开GET/api/auth/me获取当前用户信息需要 JWT文章相关接口方法路径功能权限GET/api/posts文章列表分页公开GET/api/posts/:id文章详情公开POST/api/posts发布文章需要 JWTPUT/api/posts/:id编辑文章作者或管理员DELETE/api/posts/:id删除文章作者或管理员评论和分类接口类似不一一列举。这套设计的好处是对前端非常友好接口路径和方法的含义自解释对接方不需要看完整文档也能猜个大概。3.3 全局错误处理与响应格式统一接口没报错的时候什么都好说一旦报错了返回格式乱七八糟才是灾难。我习惯把所有接口的响应统一成一个格式前端处理逻辑就非常省事。在utils/response.js里定义一个统一的响应工具// utils/response.js exports.success (res, data null, message ok) { res.json({ code: 0, message, data }); }; exports.error (res, message error, code 1, status 400) { res.status(status).json({ code, message, data: null }); };然后写一个全局错误处理中间件// middlewares/errorHandler.js const logger require(../utils/logger); module.exports (err, req, res, next) { logger.error(err.message); const status err.status || 500; res.status(status).json({ code: status 500 ? 1 : err.code || 1, message: status 500 ? 服务器内部错误 : err.message, data: null }); };errorHandler要有四个参数err, req, res, nextExpress 才能识别它为错误处理中间件。然后在app.js最后注册它顺序很关键必须在所有路由之后。4. MongoDB 数据建模与操作4.1 用户、文章、评论的 Schema 设计MongoDB 是文档型数据库没有表结构的概念但你在 Mongoose 里依然需要定义 Schema它是你在代码层面的数据结构约定。设计 Schema 是整个项目里最需要想清楚的一步。User 模型// models/User.js const mongoose require(mongoose); const userSchema new mongoose.Schema({ username: { type: String, required: true, unique: true, trim: true, minlength: 3, maxlength: 20 }, email: { type: String, required: true, unique: true, lowercase: true }, password: { type: String, required: true, minlength: 6, select: false }, role: { type: String, enum: [admin, user], default: user }, createdAt: { type: Date, default: Date.now } }); module.exports mongoose.model(User, userSchema);注意几个细节password字段我设置了select: false意思是默认查询时不要返回密码字段防止在后端日志或其他地方泄露。unique: true会在字段上建立唯一索引保证用户名和邮箱不重复。Post 模型// models/Post.js const mongoose require(mongoose); const postSchema new mongoose.Schema({ title: { type: String, required: true, trim: true, maxlength: 200 }, content: { type: String, required: true }, summary: { type: String, maxlength: 500 }, category: { type: mongoose.Schema.Types.ObjectId, ref: Category, required: true }, tags: { type: [String], default: [] }, author: { type: mongoose.Schema.Types.ObjectId, ref: User, required: true }, views: { type: Number, default: 0 }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } }); postSchema.index({ title: text, content: text }); module.exports mongoose.model(Post, postSchema);这里最核心的是category和author字段都用ObjectId类型并指定ref相当于关系型数据库里的外键。通过refMongoose 可以帮你进行关联查询populate比如查询文章的时候自动把作者的用户名带出来。4.2 Mongoose 连接与 MongoClient 选择连接数据库有两种方式用官方的mongodb驱动或者用mongoose。我强烈建议项目里用mongoose因为它提供了 Schema 校验、中间件、populate关联查询等非常方便的特性能省下不少重复代码。config/db.js的写法// config/db.js const mongoose require(mongoose); const connectDB async () { try { const conn await mongoose.connect(process.env.MONGO_URI || mongodb://localhost:27017/blog-system, { useNewUrlParser: true, useUnifiedTopology: true }); console.log(MongoDB connected: ${conn.connection.host}); } catch (err) { console.error(Error: ${err.message}); process.exit(1); } }; module.exports connectDB;这里面的useNewUrlParser和useUnifiedTopology两个配置很多人不知道是干嘛的。简单说它们是 Mongoose 为了兼容旧版和新版 MongoDB 驱动而提供的适配选项。在新版本里默认已经开启但写上不会有坏处而且能避免一些版本相关的警告。4.3 聚合管道的实际应用场景MongoDB 的聚合Aggregation是一个很强大的功能热词里有人搜mongodb 聚合函数其实就是指聚合管道。我用一个实际场景来说明管理后台首页需要展示每个分类下的文章数量。写法是这样的const result await Post.aggregate([ { $group: { _id: $category, count: { $sum: 1 } } }, { $lookup: { from: categories, localField: _id, foreignField: _id, as: categoryInfo }}, { $unwind: $categoryInfo }, { $project: { categoryName: $categoryInfo.name, count: 1 }}, { $sort: { count: -1 } } ]);这个操作分五步先用$group按分类分组并统计数量再用$lookup关联分类表拿到分类名称然后$unwind把数组拆开$project指定输出字段最后$sort按数量排序。理解聚合管道的核心是把多个处理步骤像流水线一样串联起来每一步的输入是上一步的输出非常灵活。5. 博客系统核心功能实现5.1 用户注册登录与 JWT 鉴权用户的密码绝不能明文存储这是安全底线。我用bcryptjs做哈希它和 Ruby 的 bcrypt 算法一致但不需要编译原生代码装完直接用对新手特别友好。注册逻辑// controllers/authController.js const User require(../models/User); const bcrypt require(bcryptjs); const jwt require(jsonwebtoken); exports.register async (req, res) { try { const { username, email, password } req.body; // 检查用户是否存在 const existingUser await User.findOne({ $or: [{ email }, { username }] }); if (existingUser) { return res.status(400).json({ message: 用户名或邮箱已被注册 }); } // 密码加密 const salt await bcrypt.genSalt(10); const hashedPassword await bcrypt.hash(password, salt); // 创建用户 const user await User.create({ username, email, password: hashedPassword }); // 生成 JWT const token jwt.sign( { id: user._id, role: user.role }, process.env.JWT_SECRET, { expiresIn: 7d } ); res.status(201).json({ message: 注册成功, token, user: { id: user._id, username: user.username, email: user.email, role: user.role } }); } catch (err) { res.status(500).json({ message: 服务器错误 }); } };这里bcrypt.genSalt(10)的10是加密强度参数代表计算哈希使用的迭代次数是2^10 1024次。10 是安全性和性能的折中点网上普遍推荐的默认值。JWT 是什么简单理解就是服务端在用户登录成功后签发一张通行证然后每次请求带着这张通行证服务端验证一下就行了。它包含用户 ID 和角色信息签名存在服务端所以篡改会立即被发现。注意JWT_SECRET这个环境变量一定要放到.env文件里不要硬编码在代码中。鉴权中间件// middlewares/auth.js const jwt require(jsonwebtoken); const User require(../models/User); module.exports async (req, res, next) { const token req.headers.authorization?.split( )[1]; if (!token) { return res.status(401).json({ message: 未登录无法访问 }); } try { const decoded jwt.verify(token, process.env.JWT_SECRET); req.user await User.findById(decoded.id); next(); } catch (err) { return res.status(401).json({ message: 登录已过期请重新登录 }); } };习惯上客户端传 Token 的格式是Bearer token所以我用split( )取第二段。拿到 Token 后验签、解出用户 ID、查库拿到完整用户信息挂到req.user上后面的业务逻辑里直接req.user._id就能知道当前登录的是谁。5.2 文章增删改查的权限控制文章管理接口是业务核心也是权限控制展示得最充分的地方。以更新文章为例不是登录了就能改任何文章你必须是自己文章的作者或者管理员才能修改。// controllers/postController.js exports.updatePost async (req, res) { try { const postId req.params.id; const post await Post.findById(postId); if (!post) { return res.status(404).json({ message: 文章不存在 }); } // 权限校验 if (post.author.toString() ! req.user._id.toString() req.user.role ! admin) { return res.status(403).json({ message: 没有权限修改这篇文章 }); } const { title, content, summary, category, tags } req.body; post.title title || post.title; post.content content || post.content; post.summary summary || post.summary; post.category category || post.category; post.tags tags || post.tags; post.updatedAt Date.now(); await post.save(); res.json({ message: 文章更新成功, data: post }); } catch (err) { res.status(500).json({ message: 服务器错误 }); } };这里有个细节很容易踩坑post.author是ObjectId类型req.user._id也是ObjectId但直接比较永远返回false因为它们是不同的对象实例。必须先用.toString()转成字符串再比较。我见过不少新手在这里卡住怎么想都想不明白哪里出错。列表接口加分页和条件查询exports.getPosts async (req, res) { try { const page parseInt(req.query.page) || 1; const limit parseInt(req.query.limit) || 10; const skip (page - 1) * limit; const category req.query.category; const keyword req.query.keyword; const filter {}; if (category) filter.category category; if (keyword) { filter.$or [ { title: { $regex: keyword, $options: i } }, { summary: { $regex: keyword, $options: i } } ]; } const total await Post.countDocuments(filter); const posts await Post.find(filter) .populate(author, username) .populate(category, name) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); res.json({ total, page, pages: Math.ceil(total / limit), data: posts }); } catch (err) { res.status(500).json({ message: 服务器错误 }); } };分页用skip limit在数据量小的时候够用数据量大到百万级就不太行了但博客系统远远达不到这个量级没必要过度设计。populate是关联查询指定字段username表示只返回作者的用户名避免把邮箱等敏感信息也带出去。5.3 评论功能与数据的嵌套关联评论业务有一个需要注意的地方评论本身是独立集合但展示的时候往往要连带用户信息和文章信息一起返回。评论的 Schema 设计// models/Comment.js const mongoose require(mongoose); const commentSchema new mongoose.Schema({ content: { type: String, required: true, maxlength: 1000 }, postId: { type: mongoose.Schema.Types.ObjectId, ref: Post, required: true }, userId: { type: mongoose.Schema.Types.ObjectId, ref: User, required: true }, createdAt: { type: Date, default: Date.now } }); module.exports mongoose.model(Comment, commentSchema);查询某篇文章的所有评论时一次性把用户信息带出来const comments await Comment.find({ postId }) .populate(userId, username avatar) .sort({ createdAt: -1 });这样前端拿到评论数据后直接comment.userId.username就能渲染用户昵称是不是很方便。5.4 数据校验与安全防护写接口的时候空参数校验不能靠前端自觉后端必须做。我习惯写一个简单的校验中间件比如检查必填字段是否存在// middlewares/validate.js exports.validate (fields) (req, res, next) { const missing fields.filter(field !req.body[field]); if (missing.length 0) { return res.status(400).json({ message: 缺少必填字段: ${missing.join(, )} }); } next(); };在路由中使用router.post(/, auth, validate([title, content, category]), postController.createPost);这样就保证了请求进到业务逻辑之前必须带齐所有必填字段省得在控制器里写一堆 if 判断。6. 常见问题与排查技巧实录6.1 MongoDB 连接失败排查清单这是新手遇到最多的问题报错通常是MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017。看到ECONNREFUSED基本可以断定 MongoDB 服务没有启动。按这个顺序排查确认 MongoDB 服务是否启动。Windows 下在服务应用里找 MongoDB 服务或者命令行执行net start MongoDB。确认连接字符串是否正确。localhost和127.0.0.1在某些机器上可能解析不同试试127.0.0.1。确认端口有没有被占用。执行netstat -ano | findstr :27017如果端口被别的程序占了需要改 MongoDB 默认端口。用 Compass 图形化工具连接如果 Compass 能连上问题大概率不在数据库而在你的 Node.js 进程。6.2 跨域问题与前后端联调本地开发肯定会遇到跨域问题。前端跑在http://localhost:5173Vite 默认后端跑在http://localhost:3000浏览器会拦截跨域请求。解决办法已经写在前面了用cors中间件。但有一个细节如果你需要携带 Cookie 做会话管理而不是用 JWT需要额外配置app.use(cors({ origin: http://localhost:5173, credentials: true }));注意origin要指定具体地址不能是整个*不然credentials: true会报错。6.3 数据校验与参数类型不匹配Mongoose 的 Schema 自带类型校验但有时候会踩到一些隐藏的坑。比如 Post 模型里category字段定义的是ObjectId你从前端传过来一个字符串64f2c8d2e4b0a1a2b3c4d5e6Mongoose 会自动帮你转成ObjectId。但如果字符串格式不是合法的 24 位十六进制Mongoose 会在转化时报错。这种错误很隐性因为报错信息可能只是简单说CastError。排查思路是检查请求参数里所有 ID 类型的字段格式。我写代码时习惯在路由层就打印一遍req.params和req.body开发阶段看到实际传值很多问题立刻就能发现。6.4 索引优化与查询性能备忘最后说说索引这个事。热词里有mongodb怎么建索引简单解释一下。索引是 MongoDB 加速查询的机制类似于书的目录。建立一个索引// 在 Post 模型里已经通过 index 方法建了全文索引 postSchema.index({ title: text, content: text }); // 也可以手动建索引 db.posts.createIndex({ category: 1, createdAt: -1 });第一行是文章标题和正文的全文索引支持$text搜索。第二行是分类和时间的复合索引如果经常按分类查文章并按时间排序这个索引很有效。但不要什么字段都建索引索引虽快占空间、拖慢写入适可而止。博客系统这种规模建三四个月度查询频繁的索引就足够了。6.5 密码加密与安全部署注意点最后分享几个安全性建议可能看起来不起眼但都属于吃过亏才知道的点。bcryptjs和bcrypt虽然名字很像但bcrypt依赖原生编译在 Windows 上经常装不上bcryptjs是纯 JavaScript 实现功能相同但没有编译步骤属于能用就行的优选。.env文件一定要加入.gitignore否则把项目推到代码托管平台就等于把 JWT 密钥、数据库地址全部公开了。如果你已经不小心提交了记得去平台的历史记录里清掉并立即更换密钥。Express 4 里处理异步错误需要自己包一层try/catch或者用express-async-errors这个包自动捕获。我写过太多try/catch后来直接用express-async-errors异步抛错自动交到错误处理中间件代码干净很多require(express-async-errors);启用后控制器里throw new Error(xxx)会被自动捕获统一由errorHandler处理不用每个方法都写catch。7. 项目拓展这个博客系统还能怎么玩博客管理系统做完了其实只是第一步。这个项目最大的价值是它的可拓展性接下来你可以往里面加各种功能来练手。第一给文章加上 Markdown 编辑器支持。后端只需要把content存储为 Markdown 原文本前端用marked或markdown-it做渲染列表页可以渲染摘要详情页渲染全文几十行代码就能搞定。第二加一个文件上传模块。用multer处理图片上传图片存到本地磁盘或云存储文章里通过![](图片链接)引用。这个功能几乎每个真实博客都需要涉及静态资源托管和文件类型校验是很好的进阶练习。第三做阅读统计。现在的 Post 模型里已经有views字段了但每次用户访问详情页views 1会有刷量的风险。一种简单方案是IP 文章 ID 做去重一天内同一 IP 只加一次。用 MongoDB 的 TTL 索引设置过期时间可以实现这个逻辑很有意思。第四接入 RSS 订阅。博客系统提供 RSS 订阅地址读者用 RSS 阅读器就能订阅你的文章更新。实现思路是写一个接口把文章列表渲染成 XML 格式。这算是一个小众但很实用的功能。第五部署上线。本地跑通了项目最后要部署到服务器。把环境变量配置好用pm2管理 Node.js 进程然后在 Nginx 里配置反向代理让请求从 80 端口转发到 3000 端口。如果还需要 HTTPS用 certbot 自动签发证书十几分钟就能搞定。我个人在实际操作中的体会是项目不要追求一步到位先把基础功能跑通再一个一个加模块。每加一个功能你对整个技术栈的理解就会加深一层。写完这个博客系统如果你能把每一行代码都讲清楚为什么要这么写那你的 Node.js 全栈水平就已经过了会写到懂设计的坎了。最后再分享一个小技巧开发的时候把 MongoDB Compass 开着随时看数据库里的数据变化排查问题的时候信息量比纯看代码日志要大得多。我做这个项目时好几个想不通的问题都是点开 Compass 看到实际存储的数据才恍然大悟。工具就在那里别只看代码数据才是真相。本文还有配套的精品资源点击获取
返回列表