从毕设题目到可跑通的文学交流平台:我基于 Node.js + Express 的完整实现复盘
如果你正对着“nodejs基于express的文学交流平台的设计与实现”这种题目发愁,或者已经写了好几版代码但总觉得心里没底,那这篇文章就是给你准备的。我最近刚好完整做了一个基于 Node.js 和 Express 的文学交流平台,从前端页面到后端接口、从数据库设计到部署上线全流程走了一遍,踩了不少坑,也沉淀了一些真正能落地的经验。这篇文章不搞虚的,直接把我从需求拆解到最终实现的过程拆开讲,把遇到的典型问题、排查思路和解决方案都写清楚,你们照着这个思路走,大概率能少走很多弯路。
先说清楚这个项目到底是什么。它是一个典型的 Web 全栈项目:用户可以在平台注册登录、发布文学类文章(小说、诗歌、散文、书评都行)、对别人的作品进行评论、给喜欢的作品点赞,还能按分类浏览和搜索内容。这类平台本质上就是“内容社区型的 CRUD 应用”,但它比普通的博客系统多了一层社交属性,所以权限控制和数据表设计会比单纯写文章的系统复杂一点。这个项目非常适合做毕业设计、课程设计或者作品集项目,因为它的业务场景清晰,技术栈主流,往上可拓展的空间也大——比如后续加聊天室、加入推荐系统,都能和现有架构衔接得很自然。
整篇复盘我会从六个部分来展开:先聊聊拿到题目时我是怎么做需求拆解和技术选型的,再讲项目目录结构和数据库设计,然后是核心模块的代码实现与设计原因,接着是完整的实操过程,再把我遇到过的常见问题和排查思路整理成一张速查表,最后分享几个我现在回过头看觉得最值得注意的经验。每一部分我都会尽量解释清楚“为什么这么做”,而不只是贴一段代码就完事。
1. 拿到题目后:需求拆解与技术选型
1.1 不要把题目当成文案,要把题目翻译成功能清单
第一次看到“文学交流平台”这几个字,我脑子里其实是一团浆糊的。经验告诉我,拿到这类题目第一件事不是急着写代码,而是先把它翻译成一张功能清单。我当时就是拿一张纸,把“文学交流”这四个字往细了拆:
- 用户侧:注册、登录、退出登录、个人资料查看与编辑、查看自己发布过的内容;
- 内容侧:发布文章(需要选择分类、填标题、写正文)、浏览文章列表、查看文章详情、编辑和删除自己的文章;
- 互动侧:对文章发表评论、查看评论列表、删除自己的评论、点赞/取消点赞;
- 检索侧:按分类筛选文章、按关键词搜索标题或正文、按最新/最热排序;
- 后台侧(可以后置):管理用户、审核或删除违规内容。
这一步做完,项目的大致规模就清楚了。我当时的判断是,后面四个模块是核心,后台管理可以放到最后有富余精力时再做,但前四个必须优先保证。
这里顺带讲一下我选型时的想法。题目只写了 nodejs 和 express,数据库没有指定,这意味着我可以自由选。我最终用的是 Express + MySQL + EJS 模板引擎的组合。为什么不用 React 或者 Vue?因为这类课程设计性质的项目,页面的主要目标是“能用、完整、流程闭环”,用服务端模板引擎(EJS)可以把后端渲染和前端逻辑绑在一起,减少前后端分离带来的跨域和 token 处理复杂度,能省掉非常多调试时间。但你如果对前端框架更熟,也不是不能用,改成 Express 提供 JSON API、前端单独跑一个 Vue 项目也完全可以,只是工作量会上去一截。我的建议是:如果你的重点是想展示后端能力,就选 EJS 这种传统方案;如果你想展示全栈能力且时间充裕,再考虑前后端分离。
1.2 技术栈确认与版本选型里的门道
技术栈我建议锁定在这样一套组合上,都是我实测稳定的版本组合:
- Node.js 14+ 或 16+(建议 16,兼容性最稳,18 和 20 也没问题,但个别老教程的写法在新版本里会报弃用警告);
- Express 4.x(虽然 Express 5 已经出到 beta 很久了,但网上资料和中间件生态还是以 4.x 为主,别在高版本上纠结);
- MySQL 8.x(5.7 也带得动,但 8.x 的窗口函数、JSON 字段类型对后面的评论统计、复杂查询都有帮助,建议直接 8.0);
- EJS 3.x(模板引擎,语法简单,跟原生 HTML 几乎一样);
- sequelize 或 mysql2 驱动(我实名推荐 mysql2,轻量、好用、支持 Promise,写起来比原生 mysql 包舒服太多)。
另外有个容易踩的坑是 npm 安装依赖的速度和稳定性问题。如果你在 install 的时候频繁超时或报错,建议把镜像源切换到国内镜像,命令行执行npm config set registry https://registry.npmmirror.com,这个操作能直接解决 90% 的下载失败问题。
2. 项目结构设计与数据库建模
2.1 目录结构:按“路由-控制器-服务”拆,不要全堆在 index.js 里
我在很多同学的项目里看到过这种情况:所有路由、数据库查询、业务逻辑全堆在一个app.js里,一个文件写了三四百行,看着能跑,但后续每加一个功能都要在文件里反复翻找,改出 Bug 的概率直线上升。这种代码风格,一旦面试官或答辩老师问起“你的项目是怎么组织模块的”,你很难讲出像样的设计思路。
我第一次做这个项目时用的是比较经典的三层结构,你们可以参考:
literature-platform/ ├── app.js # 入口文件:创建应用、挂载中间件和路由 ├── package.json ├── config/ │ └── db.js # 数据库连接池配置 ├── routes/ │ ├── index.js # 公共页面路由 │ ├── auth.js # 注册、登录、退出 │ ├── article.js # 文章发布、列表、详情、编辑、删除 │ └── comment.js # 评论相关路由 ├── controllers/ │ ├── authController.js │ ├── articleController.js │ └── commentController.js ├── services/ │ ├── userService.js │ ├── articleService.js │ └── commentService.js ├── views/ # EJS 模板 │ ├── layout.ejs │ ├── index.ejs │ ├── login.ejs │ ├── register.ejs │ ├── article/ │ │ ├── list.ejs │ │ ├── detail.ejs │ │ ├── create.ejs │ │ └── edit.ejs ├── public/ │ ├── css/ │ ├── js/ │ └── images/ └── middleware/ └── auth.js # 登录状态校验中间件routes 层只管“段路径对应哪个函数”,controller 层只做参数接收、调用服务、返回响应,services 层放真实的业务逻辑和数据库操作。这样分层最大的好处是可测试性高,比如你后面想把 500 行数据库查询逻辑换成缓存,只需要改 service,路由和控制器一行不用动。如果你觉得三层冗余,至少要做到“路由+逻辑”分离,千万不要把 SQL 语句写在路由回调里,这一点面试官非常看重。
2.2 数据库表设计:五张核心表与字段设计思路
表设计关系到后面所有功能的实现复杂度,我在这一块反复改过好几版,最终稳定下来的核心表结构是这样的:
- users(用户表):id、username、password(加密存储)、nickname、avatar、bio、created_at。username 做唯一索引,登录和显示名称分开是考虑到用户体验——用户名一旦注册不好改,但昵称可以随时换。
- articles(文章表):id、user_id(外键关联用户)、category(文章分类)、title、content、view_count、like_count、comment_count、created_at、updated_at。文章正文我用的是 TEXT 类型,MySQL 里 TEXT 最多存 64KB 字符,对文学类文章完全够用,不需要上 LONGTEXT。
- comments(评论表):id、article_id、user_id、content、created_at。每篇文章的评论不用单独建表,用 article_id 关联查询即可。
- likes(点赞表):id、user_id、article_id、created_at,并且要用 (
article_id,user_id) 建联合唯一索引,防止同一个人重复点赞。 - categories(分类表):id、name、description。分类数据量很小,可以预先手工插入,也可以在应用启动时初始化。
这里分享一个我实际设计时踩过的坑:最开始我在 articles 表里用了一个status字段来标记文章状态(正常/删除),但后来发现这个字段经常没被用到,反而让所有查询都要记得加WHERE status = 1,漏加就会导致已删除的文章出现在列表里。后来我把删除操作改成了物理删除,省掉了这个隐性问题。如果你的平台强调“回收站”概念,再保留 status 不迟,但没必要一开始就设计过度。
3. 核心功能模块实现与设计思路
3.1 用户注册登录:密码加密与 Session 会话方案
用户模块是所有平台的基础,这里我会把关键决策和实现技巧拆开讲。注册登录要解决的核心问题有两个:密码怎么存,登录状态怎么保持。
密码存的方案,我直接排除了 md5 和 sha1,用的 bcryptjs 这个库。为什么不用 md5?因为 md5 是快速哈希,攻击者可以用彩虹表反查,虽然加上 salt(随机盐)之后安全性会提升,但 bcrypt 本身就是为密码设计慢哈希算法,内置 salt 机制,计算速度刻意设计得慢,这让暴力破解的成本高到不划算。使用方式也简单:
const bcrypt = require('bcryptjs'); // 注册时:生成哈希 const saltRounds = 10; const hashedPassword = await bcrypt.hash(password, saltRounds); // 登录时:比对哈希 const isValid = await bcrypt.compare(inputPassword, hashedPassword);登录状态的保持方案有两条路:Session-Cookie 和 JWT。我选的是 Session。原因很简单:这个项目是服务端渲染的 EJS 页面,浏览器每次请求都会带上 Cookie,服务端通过 Session 就能识别用户身份,逻辑简单清晰。JWT 更适合前后端分离的场景。在 Express 里实现 Session 是一套非常成熟的老三样:
const session = require('express-session'); const MySQLStore = require('express-mysql-session')(session); app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: false, store: new MySQLStore(dbConfig), cookie: { maxAge: 1000 * 60 * 60 * 24 } // 24小时 }));这里有个细节:默认的内存 SessionStore 只适合开发环境,因为数据存在内存里,服务一重启登录状态全部丢失,而且内存占用会不断上涨。条件允许的话建议直接上 MySQLStore,把 Session 数据持久化到数据库表里,重启服务也不掉线,这也是答辩时一个不错的加分点。
登录后标识当前用户的方式也很直观:登录成功后设置req.session.user = { id, username, nickname },在后续任何请求里通过判断req.session.user是否存在来确认是否已登录。模板里也能直接用这个变量来显示用户昵称或“登录/注册”按钮。
3.2 文章发布与展示:文本存储、分页查询与排序策略
文章模块是整个平台的核心。发布文章的表单包含三个字段:分类(select 选择)、标题(input)、正文(textarea)。提交时后端要做两件事:验证参数合法性,写入数据库。
参数验证这一块,我强烈建议不要信任前端的任何输入,所有验证都必须在后端重复一遍。标题空字符串、内容长度小于 5 个字、分类不在预设列表里,这些都属于非法请求,直接返回错误提示。因为前端校验用浏览器开发者工具绕过太轻松了。我实际用的是 express-validator 这个库,它可以用链式调用的方式把验证逻辑写得非常清晰:
const { body, validationResult } = require('express-validator'); [ body('title').trim().isLength({ min: 2, max: 100 }).withMessage('标题长度需在2-100字之间'), body('content').trim().isLength({ min: 10 }).withMessage('正文内容不能少于10个字') ]文章列表页的实现要重点说分页。如果一上来就SELECT * FROM articles,数据量只要超过几百条,页面就会越滚越长,响应速度也肉眼可见地变慢。分页的 SQL 写法很简单:
SELECT a.*, u.nickname FROM articles a LEFT JOIN users u ON a.user_id = u.id ORDER BY a.created_at DESC LIMIT ? OFFSET ?LIMIT 是每页条数,OFFSET 是偏移量(第几页 * 每页条数 - 每页条数)。前端页码导航加上首页、上一页、下一页、末页四个按钮,配合 Express 的查询参数实现起来非常顺滑。
排序策略上,我一开始只做了按时间排序,后来加了一个“热门”排序选项,按view_count + like_count * 3这种加权值来排。加权的系数我调了好几次,最终感觉“点赞权重略高于浏览”的效果比较好,否则高浏览量但低质量的文章会一直霸榜,用户刷几次就觉得平台内容没品位,这是我观察自己部署后用户真实反馈得出的结论。
3.3 评论与点赞:事务一致性与防止重复点赞
评论和点赞虽然看起来简单,但对数据一致性要求特别高。
评论的逻辑是:用户在某篇文章详情页提交评论后,后端要做两件关联动作——插入一条评论记录,然后把文章的 comment_count 加 1。这两个动作必须在一个数据库事务里执行,不然就会出现评论插入成功但计数没更新、或者反过来计数变了但看不到评论的脏数据。用 mysql2 的 connection 来操作事务的代码如下:
const conn = await pool.getConnection(); try { await conn.beginTransaction(); await conn.query('INSERT INTO comments (article_id, user_id, content) VALUES (?, ?, ?)', [articleId, userId, content]); await conn.query('UPDATE articles SET comment_count = comment_count + 1 WHERE id = ?', [articleId]); await conn.commit(); } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); }点赞的逻辑核心是防止重复。我的做法是建唯一索引加“先查后插(有则删)”的策略。用户点击点赞按钮时,前端发送请求,后端先查 likes 表里有没有记录,有就执行删除并把文章的 like_count 减一,没有就插入一条新记录并把计数加一。这就是一个优雅的“切换点赞状态”的接口。唯一索引(article_id, user_id)保证了并发情况下也不会出现同一个人对同一篇文章点赞两条。这里我再多提醒一句:不要低估数据库索引的重要性,没有这个唯一索引的时候,连点两次按钮就能制造出脏数据,而且线上排查这种 Bug 会非常痛苦。
4. 完整实操过程:从初始化到核心接口实现
4.1 环境准备:Node.js 安装与 npm 初始化中常见的坑
Node.js 安装本身并不复杂,但这里我必须提醒几个极其常见、几乎每个新手都会遇到的坑。如果你想用 npm 命令却发现报错:
npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本这是 Windows PowerShell 的执行策略限制,不是 Node 安装坏了。解决办法是以管理员身份打开 PowerShell,执行:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned然后选“是”或“Y”确认。这个操作会允许本机运行的脚本签名验证,但禁止从网络下载的未签名脚本运行,属于安全性可接受的折中方案。
安装完 Node 后在项目目录里跑npm init -y生成 package.json,然后安装本次项目需要的基础依赖:
npm install express ejs mysql2 bcryptjs express-session express-validator如果有需要,再加开发依赖nodemon,它可以监听文件变化自动重启服务,省去每次改代码手动重启的麻烦。你需要在 package.json 里把启动脚本改成"start": "nodemon app.js",这样每次保存代码服务都会自动加载,调试体验直线上升。
4.2 数据库初始化:建库建表脚本的完整写法
数据库方面,我直接先用命令行或 Navicat 建好库,然后在应用启动时执行建表语句。库名我用的是literature_db,字符集选 utf8mb4。这里强调一下:必须用 utf8mb4 而不是 utf8,因为 utf8 在 MySQL 里实际上是 utf8mb3,它不支持存储 emoji 和部分生僻字。文学交流平台天天都有用户评论,一个表情符号把整段内容存挂是极其影响体验的问题。
建表脚本大致长这样:
CREATE DATABASE IF NOT EXISTS literature_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE literature_db; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50) DEFAULT '文友', avatar VARCHAR(200) DEFAULT '/default-avatar.png', bio VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE articles ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, category VARCHAR(20) NOT NULL, title VARCHAR(100) NOT NULL, content TEXT NOT NULL, view_count INT DEFAULT 0, like_count INT DEFAULT 0, comment_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_category (category), INDEX idx_created (created_at) ) ENGINE=InnoDB;注意文章的 title 和 content 都不是空字符串,空串也能被存进去,但这是业务逻辑问题,需要靠后端验证解决。外键ON DELETE CASCADE的意思是用户注销时他的所有文章会被一起删除,避免留下孤儿数据。这个行为在早期可以粗放一点,但如果你做的是真实可运营平台,更稳妥的做法是软删除或者把外键改成SET NULL,这个细节可以根据你项目的答辩侧重点取舍。
4.3 核心接口实现:登录校验中间件与文章详情页的完整响应
这个项目里最有价值的代码不是实现某个功能的简单路由,而是如何优雅地复用登录校验逻辑。我写了两个中间件,一个是requireLogin,一个是requireOwnership:
// middleware/auth.js function requireLogin(req, res, next) { if (req.session.user) { next(); } else { res.redirect('/login?redirect=' + encodeURIComponent(req.originalUrl)); } } function requireOwnership(req, res, next) { const articleId = req.params.id; // 查询文章并比对 user_id 与当前登录用户 id // 不匹配就返回 403 页面 next(); }这样的中间件可以用在任何需要登录才能访问的路由上,一行代码就能声明清楚:router.get('/create', requireLogin, articleController.showCreatePage)。后续你加“编辑资料”“删除评论”等功能时,同样的权限逻辑直接复用,代码总量和 Bug 数量都会显著下降。
文章详情页是功能最密集的页面,完整流程是这样的:后端接收文章 id,先执行一条 UPDATE 语句,把 view_count 加一,然后再执行 SELECT 把文章详情和作者信息查出来,接着执行 SELECT 把评论列表带出来,最后渲染到 EJS 模板。一个页面上交互了三个查询,需要注意 SQL 的执行顺序:先更新浏览量再查询,这样页面上显示的数字才是包含本次访问的最新值。不过这里要提醒一点,把view_count的更新放在每次访问请求里的写法在高并发下会有性能损耗,但这是后置优化问题,课程设计阶段完全够用。
4.4 异常处理与错误页:不要让你的平台在崩溃时一片空白
Express 默认的错误处理是返回一个 HTML 错误页,但默认页面极其简陋且不友好。我在项目里做了一套统一错误处理中间件:
app.use((err, req, res, next) => { console.error(err.stack); res.status(500).render('error', { message: '服务器开小差了,请稍后再试' }); });所有业务逻辑里的throw new Error(...)都会被这个中间件捕获,用户看到的是一个友好的提示页面,而不是一堆堆栈信息。开发调试阶段你会想看到完整的日志,所以这里加一行console.error(err.stack)是非常必要的,日志是排查线上问题的第一手情报。
另外,404 页面也值得花两分钟设置。把所有无法匹配到路由的请求都渲染一个“页面不存在”的模板,同时给出返回首页的链接,用户的体验分能提升不少。
5. 常见问题与排查技巧实录
说实话,这个项目调试过程中遇到的大部分问题都不是什么高深算法,反而是环境配置、语法细节和数据库层面的常见坑。我整理了一个速查表,你们做的时候可以直接对照排查。
| 现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
| npm 安装依赖报错 ERR_SOCKET_TIMEOUT | 网络连接不到 npm 源 | 切换镜像源npm config set registry https://registry.npmmirror.com |
| npm 报无法加载 npm.ps1 | PowerShell 执行策略限制 | 管理员身份运行Set-ExecutionPolicy RemoteSigned |
| 启动服务后浏览器访问 localhost:3000 白屏 | 端口被占用或路由未注册 | 改端口(app.listen(3001))或检查路由是否app.use挂载 |
| 登录后刷新页面又变回未登录 | Session Store 使用内存模式 | 换成 MySQLStore 持久化 Session,或在单机部署时检查 Cookie 是否成功写入 |
| 文章内容存进去了但页面显示乱码 | 数据库字符集不是 utf8mb4 | 建库时指定字符集,或执行ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4 |
| 查询文章列表时报 “Unknown column” | 表结构跟查询字段不一致 | 先DESC 表名查看结构,确认字段名拼写和大小写 |
| 发布文章时一直不跳转、也没有错误提示 | 表单提交路径与路由不一致 | 检查 form 的 action 路径是否与 router 的路径完全匹配(注意前后斜杠) |
| 点赞按钮点了没反应 | 前端脚本请求接口路径写错 | 打开浏览器开发者工具,看 Network 标签里请求的状态码和响应体 |
| 无法连接数据库 ECONNREFUSED | MySQL 服务没启动或端口不对 | 检查 MySQL 服务状态,确认端口是 3306,用户名密码是否正确 |
有几个问题我单独展开说说。
第一个是端口问题。很多同学喜欢固定用 3000,但系统其它服务(比如某个前端项目)时不时也会占用这个端口。启动时报错Error: listen EADDRINUSE: address already in use,要么换端口,要么杀掉占用的进程。在 Windows 上查找占用端口的命令是:
netstat -ano | findstr :3000然后记下最后一列的 PID,用taskkill /PID 这里是PID /F强制干掉它。Linux/macOS 上位命令是lsof -i :3000和kill -9 PID。
第二个是“中文内容存入 MySQL 报错 Incorrect string value”。这个基本就是字符集问题。如果你在建数据库时偷懒用了默认字符集(通常是 latin1),存中文必炸。前面讲的 utf8mb4 一定要在建库时就设置好。如果库已经建了,可以这样补救:
ALTER DATABASE literature_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;另外连接字符串里也要指定 charset:
const dbConfig = { host: 'localhost', user: 'root', password: '123456', database: 'literature_db', charset: 'utf8mb4' };三是时间显示问题。MySQL 的TIMESTAMP类型默认存储的是 UTC 时间,而你在东八区,直接查出来显示在页面上会看到比本地时间慢 8 个小时。解决办法有两类:一是数据库连接时设置timezone: '+08:00',让驱动在取数据时自动做时区转换;二是查出来后手动格式化。如果你用的mysql2,连接配置里加一行timezone: 'Z'或者timezone: '+08:00'都能解决。千万不要为了图省事直接在 SQL 里DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s'),这种做法会导致排序和后续时间操作的不一致,到时候改起来非常难受。
第四个是安全问题,评论区里被人发脚本这事真实发生过。核心原因是直接把用户输入的 HTML 拼接进了页面。解决方式有两种:一种是在渲染时对所有变量做转义,EJS 默认<%= %>会转义,但<%- %>不会,你要特别注意别误用;另一种是在后端入库前就过滤掉危险标签。更稳妥的做法是两者都做。我项目里用的方法是后端在服务端渲染时统一转义,凡是用<%- %>输出用户内容的代码全部改成<%= %>,保证安全后再考虑富文本需求,如果有富文本就引入 xss 过滤库。
6. 部署上线与后续扩展的经验分享
6.1 本地测试与部署的基本流程
开发完代码后,我建议你按照下面的顺序做一遍完整的回归测试:注册新账号、登录、修改资料、发文章、编辑文章、评论、点赞、退出、重新登录、验证 Session 是否还在。这些是全部核心链路,只要它们全通过了,项目就算基本达标。
部署的话,最省事的方案是在本地 Windows/Linux 上直接跑 Node 服务,配合 Nginx 反向代理(Nginx 负责监听 80 端口,转发到 Node 的 3000 端口)。如果你有云服务器,还可以用 PM2 做进程守护,这样服务崩溃后会自动重启,不会出现“人不在服务器边,服务挂了没人管”的尴尬情况。
PM2 的常用命令极简,三行就够了:
npm install -g pm2 pm2 start app.js --name literature pm2 save还有一个部署细节:把项目里的敏感配置(数据库密码、Session secret)放到环境变量里,而不是硬编码在代码中。比如用.env文件,配合dotenv这个库加载,然后代码里process.env.DB_PASSWORD。这个习惯在答辩时提出来非常加分,它体现了你对配置管理和信息安全的意识。
6.2 从毕设到真实项目的扩展思路
如果你的项目想表现得更“能打”,下面这几个点可以作为后续迭代的方向:
- 引入分页组件优化文章列表和评论列表,前端加一个“加载更多”按钮而非纯翻页;
- 增加“个人中心”页面,展示我发布的文章、我的评论历史,再来一个简单的统计面板(发布总数、获赞总数);
- 把分类从硬编码改成后台可维护,同时加上“热门分类”排行;
- 接入 Markdown 编辑器,让正文支持富文本排版,这会明显提升文学类内容的阅读体验;
- 加入全文搜索(MySQL 的 LIKE 配合全文索引,或者引入 Elasticsearch,但不是必须);
- 做一个简单的后台管理页面,支持管理员登录后查看文章列表、删除违规内容、封禁用户。
不要一口气全做完,挑一两个感受一下,把一个扩展点做深做透,比你堆十个半成品功能更有说服力。
写在最后的一点心得
这个项目给我的整体感觉是:表面上看是一个标准的“增删改查”系统,但真正动手做起来,把用户登录态、数据一致性、权限校验、页面渲染这些环节全部串在一起时,还是有很多细节值得反复打磨。我做第一版的时候也犯了“重页面轻逻辑”的毛病,把大量精力花在调 CSS 上,后来发现几个页面之间的跳转逻辑拧成一团,不得不重构,才真正意识到好的项目架构能省下多少后期时间。
如果你正在做类似的题目,我给一句掏心窝的建议:先确定好功能清单和数据表结构,再写任何业务代码。表结构一旦定下来,整个项目的主心骨就稳定了,后面再怎么改都只是加加减减。数据表设计阶段多花一小时,后期能少熬三个通宵,这是我踩过多次坑之后最深的体会。希望这篇复盘能帮你顺利跑通这个项目,也欢迎在评论区聊你在实现过程中遇到的卡点,我尽量回复。