ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flask + Vue 前后端分离实战:从零搭建读书分享评论系统

Flask + Vue 前后端分离实战:从零搭建读书分享评论系统 从零搭建一个读书分享评论系统Flask Vue 的前后端分离实践在知识类社区越来越受关注的当下做一个轻量级的读书分享评论系统既能满足自己记录阅读心得的需求也是练手前后端分离开发的一条很合适的路径。这个项目用python flask vue做技术栈后端提供 REST API前端用 Vue 组件化构建界面麻雀虽小五脏俱全正好可以把 Python 基础、Flask 接口开发、Vue 组件通信、数据库建模这些知识点串起来。这篇文章会从需求拆解讲到数据库设计从接口实现讲到前端页面再补上部署环节的实操踩坑记录。不管你是刚学完 Python 语法想找个完整项目练手还是已有 Flask 经验想补 Vue 前端都可以按这篇文章的思路一步步复现出来。1. 项目背景与整体设计1.1 为什么选读书分享评论这个方向做练习项目最忌功能堆砌。我见过很多人一上来就搞商城、搞社交平台结果光权限和支付那块就劝退了。读书分享评论这个方向有一个很关键的优点业务逻辑足够完整但复杂度可控。一次完整的读书分享核心动作就是分享一本书 写一段感想 其他人可以评论。这里面涉及到的数据实体有用户、书籍、分享帖、评论涉及到的操作有注册登录、发帖、回帖、点赞如果你想加的话、搜索。整套流程刚好覆盖了一个典型 Web 应用的所有核心环节又没有电商那种订单状态机复杂度。从学习角度说这个项目能练到的点非常集中后端Flask 路由设计、SQLAlchemy 数据建模、用户认证session 或 JWT、RESTful API 规范前端Vue 组件化拆分、父子组件通信、Vue Router 页面路由、Axios 接口调用工程化前后端分离开发、跨域处理、生产环境部署我在实际开发中把用户体系、分享模块、评论模块分成三个大块每个大块独立完成后先本地测通再往下走。这个节奏很重要如果你一口气把代码全写完再调试出错了很难定位。1.2 技术选型的逻辑为什么是 Flask Vue 而不是别的组合选技术栈不能只赶时髦得看项目体量和你当前的学习阶段。后端用 Flask我个人觉得它最大的优势是小而不简陋。相比 Django 那种全家桶Flask 的核心只做路由和请求分发ORM、表单校验、登录认证这些都可以按需引入。对于这个读书分享评论系统来说Flask 的灵活度刚好够用——你需要什么装什么包体积小启动快代码读起来也直白。如果你以后想往大项目走Flask 的很多设计思想蓝图中模块化、上下文管理、装饰器扩展和 FastAPI 是相通的迁移成本并不高。前端用 Vue 而不是原生 JS原因很实在页面里有大量的动态数据渲染。一本书的详情、评论列表的更新、表单的状态维护如果全用 DOM 操作去做代码不仅长而且容易乱。Vue 的响应式数据绑定可以把关注点从怎么操作 DOM转移到数据是什么样这对新手来说是一个非常友好的心智模型。前后端分离的意思就是Vue 跑在 5173 端口开发环境Flask 跑在 5000 端口两边通过 HTTP 接口通信。浏览器访问 Vue 页面页面里的 JS 代码去请求 Flask 的接口拿数据然后渲染成界面。开发时用 Vite 代理解决跨域生产时用 Nginx 把两个服务统一到同一个域名下。这套架构是目前中小型 Web 项目的标准范式从这个项目里练熟之后出去看其他项目基本都能看懂。2. 后端设计与核心实现2.1 数据库模型设计四张表把业务关系理清楚数据库设计是整个项目的基石。我的建议是动手写代码之前先把 ER 图草稿画出来。这个项目的核心实体有四个用户、书籍、分享帖、评论。如果你不想单独维护一张书籍表也可以把书籍信息直接嵌在分享帖里。但那样做是偷懒因为同一本书可能被多个用户分享评论也要挂在特定的分享下。把书籍独立成表通过外键关联到分享帖数据结构更清晰后面做按书籍聚合所有分享这种功能时才不用改表结构。具体到字段设计我是这么定义的用户表users 主键 id、用户名 username唯一、密码哈希 password_hash、头像链接 avatar_url、创建时间 created_at书籍表books 主键 id、书名 title、作者 author、封面图片链接 cover_url、简介 summary、ISBN可选、创建时间 created_at分享表posts 主键 id、外键 user_id 关联用户、外键 book_id 关联书籍、分享标题 title、内容 content长文本、点赞数 like_count、创建时间 created_at评论表comments 主键 id、外键 post_id 关联分享、外键 user_id 关联用户、评论内容 content、创建时间 created_at用 SQLAlchemy 定义模型时有几个细节要注意。外键关联要写清楚relationship和backref这样查询时可以很方便地做联表操作而不是手动去查两次。比如某个用户的全部分享可以直接user.posts拿不需要再写一条Post.query.filter_by(user_idxxx)。这个设计会直接影响后面写接口简洁度。密码一定要存哈希值不能存明文。Flask 里最顺手的是Werkzeug自带的安全哈希工具generate_password_hash负责加密check_password_hash负责校验。注册时加密存储登录时校验哈希值是否匹配。哈希校验这个过程是不可逆的也就是说即使数据库泄露攻击者也拿不到原始密码。时间字段建议用datetime.utcnow而不是datetime.now。这个细节我吃过亏datetime.now()取的是服务器本地时间如果你的服务器时区设置不对存进去的时间和实际时间对不上。用 UTC 统一存储展示时再在前端转成本地时间格式这套规范在任何项目里都通用。2.2 Flask 接口设计遵循 RESTful 风格写清晰易懂的 API接口设计是这个项目的灵魂。前端所有数据操作都是通过接口完成的接口设计的合理程度直接决定了前端写代码时的心情。我的接口清单如下POST /api/auth/register 注册 POST /api/auth/login 登录 POST /api/auth/logout 退出登录 GET /api/user/info 获取当前登录用户信息 GET /api/books 获取书籍列表支持搜索关键字 POST /api/books 新增书籍 GET /api/books/{id} 获取单本书详情 GET /api/posts 获取分享列表支持分页 POST /api/posts 发表分享 GET /api/posts/{id} 获取分享详情含评论 DELETE /api/posts/{id} 删除自己的分享 POST /api/posts/{id}/comments 发表评论 DELETE /api/comments/{id} 删除自己的评论有几个设计原则值得展开讲一下。资源用名词复数操作通过 HTTP 方法区分不要在 URL 里写getPosts、deleteComment这种动词式接口。RESTful 风格的好处是接口自解释拿到 URL 和请求方法就能猜出它是干嘛的。接口只操作资源不掺业务逻辑。比如发表评论这个动作后端要做的事情是登录校验 → 找到对应的分享帖 → 创建评论记录 → 返回评论对象。业务校验比如评论不能为空、不能重复提交放在接口层做但点赞数计算这种聚合操作可以作为独立接口或者事务处理不要耦合在基础 CRUD 里。统一返回格式。我在项目里所有的接口都按下面结构返回{ code: 0, message: ok, data: { } }错误时{ code: 40001, message: 评论内容不能为空, data: null }code 为 0 表示成功非 0 表示业务错误HTTP 状态码也同步区分200、400、401、403、404、500。这样做的好处是前端拦截 Axios 响应时只需要判断 code 字段业务错误和网络错误就能清晰分离。2.3 用户认证实现Session 还是 JWT 怎么选小项目做认证无非两条路Session 或者 JWT。二者本质区别在于Session 的登录状态存在服务端客户端只拿一个 session IDJWT 的登录状态存在客户端服务端通过验签来信任它。对于这个读书分享评论系统我个人推荐先用 Session Cookie 的方案原因有两个Flask 对 Session 有原生支持实现代码量很小不需要引额外的库Session 方案的逻辑直观前端只需要带上 Cookie 就行不用自己管理 token 的存取、刷新、过期。等以后你真正做前后端完全分离的大型项目再切 JWT 也不迟因为 JWT 在横向扩展、移动端支持方面确实更合适。但要注意Flask 默认的 Session 是客户端会话也就是把数据签名后存在 Cookie 里。这种方式适合存用户 ID 这种小数据千万别塞大对象。我的做法是登录成功后只做一件对的事session[user_id] user.id后续判断是否登录就去session.get(user_id)取。这样接口层可以写一个装饰器统一处理登录校验from functools import wraps from flask import session, jsonify def login_required(view_func): wraps(view_func) def wrapped(*args, **kwargs): user_id session.get(user_id) if not user_id: return jsonify(code401, message请先登录, dataNone), 401 return view_func(*args, **kwargs) return wrapped这个方案里还有一个点容易被忽略跨域请求时 Cookie 的携带问题。Flask 后端的 CORS 配置需要设置supports_credentialsTrue前端的 Axios 请求需要设置withCredentials: true两边缺一个登录状态都存不下来。这个问题在前后端分离开发时几乎必踩我会在后面的常见问题章节展开聊。2.4 分享与评论的后端实现事务处理的细节分享帖和评论是业务的核心实现时有一个绕不开的场景删除分享后它下面的评论怎么办显而易见应该一并删除否则数据表里会留下孤儿评论。在 SQLAlchemy 里这个问题有两种处理方式第一种数据库级联删除。在Post模型的评论关系里加上cascadeall, delete-orphan当帖子被删除时ORM 会自动把关联的评论删掉。这种方式省心但如果你以后从 ORM 迁移到原生 SQL得记得在数据库外键上显式加ON DELETE CASCADE容易漏。第二种手动显式删除。先查出该分享下的所有评论 ID一次批量删除再删分享。双向可控任何场景都适用。我在这个项目里用的是第二种因为代码逻辑一目了然调试时也能看到明确的删除语句。批量删除用in_操作Comment.query.filter(Comment.post_id post.id).delete(synchronize_sessionFalse) db.session.delete(post) db.session.commit()注意这里db.session.delete(post)不要漏写好多人删完评论就提交事务了帖子还留在库里。另外synchronize_sessionFalse参数的含义是告诉 SQLAlchemy 跳过当前会话中对象的同步因为你删的是符合条件的一批记录而非单个对象用这个参数可以避免性能下降和意外报错。发表评论的流程也不复杂但对输入校验有要求。我在后端做了三件事校验内容非空、校验分享存在、限制内容最大长度比如 2000 字。前端可以再做一遍校验用来提升用户体验但后端校验才是安全底线永远不要信任前端传来的数据。3. 前端架构与页面实现3.1 Vue 项目搭建与路由规划Vue 部分我用的构建工具是 Vite创建项目的命令很简单npm create vitelatest book-frontend -- --template vue创建完成后把 Vue Router 和 Axios 装上npm install vue-router4 axios前端工程结构建议按功能模块分目录而不是按文件类型堆砌。我的项目结构长这样src/ ├── api/ # 接口调用封装 │ ├── auth.js │ ├── books.js │ └── posts.js ├── router/ # 路由配置 │ └── index.js ├── views/ # 页面级组件 │ ├── Login.vue │ ├── Register.vue │ ├── BookList.vue │ ├── PostList.vue │ ├── PostDetail.vue │ └── PublishPost.vue ├── components/ # 通用子组件 │ ├── NavBar.vue │ ├── CommentList.vue │ ├── CommentItem.vue │ └── BookCard.vue ├── App.vue └── main.js按页面和组件划分目录找代码时直觉就能定位不用去猜。路由配置我做了一套带守卫的规划/books和/posts是公开页面但发表分享/publish和个人中心/profile必须登录才能访问。这就在路由层挡住了未登录用户的跳转操作。const router createRouter({ history: createWebHistory(), routes: [ { path: /, redirect: /books }, { path: /books, component: BookList }, { path: /posts, component: PostList }, { path: /posts/:id, component: PostDetail }, { path: /publish, component: PublishPost, meta: { requiresAuth: true } }, { path: /login, component: Login }, { path: /register, component: Register } ] }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) || sessionStorage.getItem(user_id) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })登录状态这里要注意一下如果你后端用 Session前端localStorage没有 token但登录后 Cookie 里会自动带上会话标识所以前面说的withCredentials配置尤其重要。如果后端把用户 ID 或者用户信息放到了 Session 里建议后端提供一个/api/user/info接口前端路由跳转时调用它判断是否真的已登录而不是只靠本地有没有存值来判断。3.2 核心页面组件拆分与数据流设计页面拆分的核心原则是一个组件只做一件事。拿分享详情页举例我把页面拆成四块PostDetail.vue路由页面负责按postId拉取数据起数据协调作用PostContent.vue展示分享正文和书籍信息纯展示组件CommentList.vue评论列表容器接收评论数组并循环渲染CommentItem.vue单条评论负责删除操作和点赞交互组件之间的数据流靠 Props 向下传数据、Emits 向上抛事件来维护。父子通信的具体写法是这个项目的核心代码实践。评论列表容器和评论子组件的通信长这样template div classcomment-list CommentItem v-forcomment in comments :keycomment.id :commentcomment deletehandleDelete / /div /template script setup import CommentItem from ./CommentItem.vue const props defineProps({ comments: { type: Array, required: true } }) const emit defineEmits([delete]) function handleDelete(commentId) { emit(delete, commentId) } /script子组件内部收到comment对象就直接渲染不需要自己去查数据。这样设计的好处是测试和复用好给CommentItem传一个假评论对象它就能独立渲染不依赖任何全局状态。收藏和点赞这类状态会出现在多个页面这时候才需要引入状态管理工具 Pinia。比如当前登录用户是谁这种全局信息在 NavBar 和发布页都要用用defineStore定义一份认证状态各处调用会方便很多。3.3 Axios 封装统一处理认证和错误Axios 不封装直接用会写出大量重复代码。每个请求都要处理 loading、错误提示、401 跳转这些逻辑抽出来放在拦截器里最干净。我在utils/request.js里做了一套统一封装import axios from axios import router from ../router const request axios.create({ baseURL: /api, timeout: 10000, withCredentials: true }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 0) { alert(res.message) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { router.push(/login) } return Promise.reject(error) } ) export default request把baseURL设成/api是在开发阶段发给 Vite 代理生产环境发给 Nginx 代理后端真实接口路径统一挂在/api下。这样就绕开了开发环境的跨域配置也方便后续环境切换不需要改业务代码。接口封装文件里每个模块导出的是带语义的函数页面组件里直接调用import { getPostDetail, createComment } from ../api/posts async function loadPost() { const data await getPostDetail(route.params.id) post.value data.post comments.value data.comments }这样页面代码看着非常干净出问题也能快速定位是接口封装还是组件逻辑的事。4. 核心功能流程实操4.1 发表一次读书分享的完整链路发表分享这个功能涉及前端表单、前端校验、后端接口、数据库写入整条链路很适合作为第一个联调功能。前端表单页有四个字段书籍选择下拉框、分享标题、分享内容、评分可选。书籍选择我实现的是异步搜索用户输入关键字前端调/api/books?keywordxxx接口把匹配结果的标题渲染出来供选择。这里有个交互细节搜索接口要防抖不要用户每敲一个字母就发一次请求。300 毫秒的防抖就够用。表单校验在前端做两层必填项检查标题非空、内容至少 10 个字长度检查标题不超过 50 字。通过后把数据POST到/api/posts后端如果校验通过会返回新建的帖子对象前端拿到结果后就跳转到新帖子的详情页。这里有一个容易踩坑的地方前端表单提交的按钮要加 loading 状态和后端连不上或者崩溃时依然会再点一次导致帖子被提交两次。解决方法是提交函数开头设置submitting true接口返回后无论成功失败都置回false按钮用:disabledsubmitting控制禁用。这个习惯在任何有提交操作的项目里都用得上。4.2 评论与书评的嵌套交互评论模块是书评系统的核心。我在设计时把评论和书评分开对待书评挂在书籍维度评论挂在分享帖维度。这样数据层次清楚进入一本书的详情页可以看到所有人对这本书的书评进入某个分享帖可以看到这个帖子下面的评论。书评的发布走的是/api/books/{id}/reviews接口字段包括评分和正文。前端用一个评分组件显示五颗星鼠标悬停预览分数点击确认。评分在前端只是数字在后端校验时限制在 1 到 5 的整数区间非法值一律拒绝。评论列表需要考虑嵌套回复吗我建议第一版先做一级评论不要做楼中楼。原因很实际楼中楼需要额外设计 parent_id 字段和递归查询前端要缩进处理工作量至少翻倍。先把一级评论的增删改查做顺以后要扩展再加字段就行。评论交互的实时刷新设计为发表评论成功后前端把新评论对象unshift头部插入到评论列表数组中同时清空输入框。这个操作最好在接口返回的评论对象里带上评论者的头像和用户名前端直接渲染不用再建 map 去查用户信息。删除评论有个权限问题只有评论作者或帖子作者可以删。后端校验基于当前用户 ID和评论的user_id比对不一致就返回 403。前端做同样的按钮显隐控制但后端校验才是真正的防线。4.3 搜索与分页列表页的实用优化列表页书籍列表、分享列表都会遇到两个问题数据多了卡、用户想筛选。我的方案是后端做分页前端做搜索框两者通过查询参数组合。后端分页接口返回结构固定为{ items: [], total: 100, page: 1, page_size: 10, pages: 10 }前端拿到total和pages后驱动分页组件点击页码时重新拉取对应页的数据。这个模式就是标准的服务端分页比一次性返回全部数据再前端做分页要科学得多数据量大时差距尤其明显。搜索功能我用的是 SQLAlchemy 的ilike模糊查询大小写不敏感用户输入python或Python都不影响结果keyword request.args.get(keyword, ).strip() query Book.query if keyword: query query.filter(Book.title.ilike(f%{keyword}%)) books query.order_by(Book.created_at.desc()).paginate(pagepage, per_pageper_page)这里有一个容易被忽略的细节搜索关键字要过滤空值和首尾空格否则用户敲了个空格进去前端传回一个空字符串查出来的结果就是全部数据很影响体验。列表渲染性能上有个 Vue 的天然优化点组件上绑定key属性时用业务主键id不要用数组索引。用索引当 key 会导致列表项复用错位尤其当列表被插入或删除元素时DOM 更新会出现诡异的状态残留。这个坑我初学时分页切换后复选框状态全乱了排查半天才发现是 key 的问题。5. 构建部署与跨域处理5.1 开发环境的 Vite 代理配置前后端分离开发时前端页面跑在 5173后端接口跑在 5000浏览器直接发请求会碰到跨域。解决思路有两种后端配 CORS或者前端配代理。我的建议是开发环境用代理让请求在服务端转发看起来就像同源请求。Vite 的代理配置写在vite.config.jsexport default defineConfig({ server: { proxy: { /api: { target: http://localhost:5000, changeOrigin: true } } } })这段配置的意思是前端页面里所有以/api开头的请求都会被 Vite 开发服务器转发到http://localhost:5000。前端代码里请求路径写/api/books就行不用写全路径。changeOrigin: true这个选项要开着它的作用是把请求头里的Host字段改成目标域名否则后端如果做了域名白名单校验请求会被拦下来。如果你非要在后端用 CORS 方案注意 Flask 里面flask-cors库的配置要带上supports_credentialsTrue否则带 Cookie 的请求发不出去。但前后端分离项目我始终推荐代理优先因为 CORS 在后端开放所有域名有安全风险。5.2 生产部署Flask 托管 Vue 构建产物生产环境我推荐一个最省事的方案前端构建成静态文件直接交给 Flask 托管整个项目跑在一个服务上。前端构建命令npm run build构建产物在dist目录里。Flask 那边把dist当成静态文件目录from flask import send_from_directory app.route(/) def index(): return send_from_directory(dist, index.html) app.route(/path:path) def static_files(path): return send_from_directory(dist, path)这个方案的坑在于Vue Router 用的是history模式路由路径是/posts/3这种形态。用户直接访问/posts/3时Flask 需要能把这个路径映射回index.html否则会 404。上面用path:path通配符的原因就是把所有未匹配的后端路由全部转发回前端入口。更稳妥的部署方案是用 Nginx 托管前端静态文件、反向代理后端接口。但这个小项目直接用 Flask 托管完全够用一台服务器跑一个 Python 进程就完事省一台 Nginx 的配置精力。5.3 服务器部署注意事项如果你准备把项目部署到云服务器上有几个实际问题分享一下。Flask 自带的开发服务器绝不能用于生产环境它的性能和安全都不达标。生产环境我用了gunicorngevent的组合gunicorn -w 4 -k gevent -b 0.0.0.0:5000 app:app4 个工作进程每个进程独立处理请求。线程模型选gevent是因为它能处理比较大的并发量而且部署简单不需要额外装 uwsgi。环境变量管理也很重要。密钥、数据库地址、端口这些不要硬编码在代码里用环境变量或者.env文件统一管理import os from dotenv import load_dotenv load_dotenv() SECRET_KEY os.getenv(SECRET_KEY, dev-secret-key)这样拿到代码的人不会误用你的生产密钥你换环境部署也不需要改代码改配置就行。5.4 数据库初始化与迁移项目里我用了 Flask-SQLAlchemy 建表第一次运行时直接db.create_all()。但后续如果改了模型字段比如给帖子加了个tags字段create_all不会帮你改已存在的表。所以项目最好引入 Flask-Migrate 做数据库迁移。Flask-Migrate 的使用流程很简单flask db init # 初始化迁移目录 flask db migrate -m add posts # 对比模型生成迁移脚本 flask db upgrade # 执行迁移还有一个我自己常犯的错误开发时数据库和数据实时在改结果提交代码时把instance/目录也提交进去了里面可能是本地数据库文件。这个目录一定要加到.gitignore里否则协作时别人拉你的代码会覆盖自己的本地数据。6. 常见问题与排查实录6.1 前端启动失败端口占用与依赖版本冲突Vite 起不来的最典型原因是端口被占用。前端默认端口 5173 被别的进程占用了之后Vite 会提示换个端口但如果你没注意到它换成了 5174回头访问页面时用的还是旧端口白白浪费时间。建议启动时看终端输出的实际访问地址。依赖版本冲突最常发生在node_modules整个目录损坏后。解决办法很简单但高频受用先删掉node_modules和package-lock.json再重新安装。rm -rf node_modules package-lock.json npm install不要用npm update去修那个命令有时会把依赖升级到不兼容的版本反而引发更多问题。6.2 后端接口乱码和 500 错误排查中文数据乱码是老熟人常见场景有两个数据库返回的字符是乱码或者接口返回 JSON 里中文变成\uXXXX转义序列。数据库乱码的问题根源多半是数据库连接字符串没指定charset。MySQL 的连接串要加上参数SQLite 一般没有这个问题。另外建表时表结构也要确认是utf8mb4它比utf8多了对 Emoji 表情的支持现在用utf8很容易在用户输入表情时直接报错。接口返回 JSON 里的中文转义是 SQLAlchemy 序列化时的ensure_ascii默认行为把返回值的JSON_AS_ASCII设为False就好app.config[JSON_AS_ASCII] False500 错误排查的第一步是打开调试开关或者看日志。Flask 开发时建议设置app.debug True报错信息会直接打在浏览器和终端里。生产环境把日志输出到文件出错后第一时间tail -100日志文件片段比在终端干瞪眼强多了。6.3 跨域和会话保存问题实录这是前后端分离开发中遇到最多的一个问题现象是登录接口调用成功但后续请求全部 401。排查步骤按顺序来第一步检查浏览器开发者工具 Network 面板里的请求。看登录请求和后续请求对应的 Cookie 字段有没有变化。第二步确认 Axios 是否带上了withCredentials: true。在utils/request.js的create()配置中设置一次即可。第三步确认 Flask 的 CORS 配置允许携带凭证。如果你后端装了 flask-cors配置写法是from flask_cors import CORS CORS(app, supports_credentialsTrue)第四步检查开发环境的 Vite 代理有没有正确把/api转发到后端地址。如果代理没生效前端请求实际上发到了http://localhost:5173/api/xxxVite 直接返回前端自己的 index.html 内容接口路径找不着表现就是 404 或者返回 HTML 格式的异常数据。具体到 Session 方案还有一个特殊点浏览器对 Cookie 的存储策略是分域名和路径的。前端跑在 5173、后端跑在 5000属于不同端口Cookie 可能无法正确传递。正是基于这个原因我才强烈建议开发环境优先用 Vite 代理让前后端在浏览器视角下处于同一个源下从根上避开 Cookie 跨域传递的问题。6.4 功能完善方向从练习项目到完整产品这个读书分享评论系统做到这里已经具备了一个最小可行产品的全部能力但它距离真正能上线的社区产品还有一段路。这里我把自己踩过的、以及周围朋友经常踩的扩展方向整理一下供你按需选择。第一用户体系可以继续丰富。目前只有注册登录和头像可以加关注关系、个人书单、阅读进度记录、积分体系。每加一个模块你都能锻炼一次数据库关系和接口设计能力。第二内容维度可以加强。目前分享和评论都是纯文本可以加图片上传书籍封面、阅读笔记照片、Markdown 编辑、富文本渲染。图片上传建议用腾讯云 COS 或者阿里云 OSS 的直传方案千万别把图片存到本地服务器存了你就等着磁盘被打满。第三推荐算法是个很好的进阶方向。基于你收藏的书评和关注的人做一个简单的相似用户读了什么推荐逻辑可以从同标签聚合开始做后续升级到协同过滤。这一块涉及 Python 数据处理能力对于想把 Python 往前走的人是很自然的一个延伸。关于这个项目的最终心得把 Flask 后端和 Vue 前端完整地联调跑通是我做这个读书分享评论系统时最有成就感的一刻。这个项目虽然小但它完整覆盖了一个 Web 应用的诞生全程从需求分析、数据库设计、接口设计到前端组件拆分、状态管理、跨域联调再到最后的生产部署和问题排查。我在实际开发中感受最深的一点是前后端分离不是多几个文件那么简单而是整个思维方式的变化。后端只需要关心数据和规则前端只需要关心界面和交互二者通过明确的接口契约来协作。刚开始联调时我总是不自觉地在前端里拼 SQL、在后端里写 HTML这个毛病要刻意改。另外别怕踩坑。跨域、乱码、Cookie 丢失、端口占用这些看起来不起眼的问题每一个都会让你对 Web 运行机制理解得更透彻。很多你现在觉得麻烦的报错一年后回头看会发现它们都是最好的老师。做完这个项目你对 Python、Flask、Vue 三者的理解都会有一个质的提升接下来不管是往数据分析方向走还是继续深耕 Web 全栈都会非常顺。
返回列表