ARTICLE DETAIL

资讯详情

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

Flask+Vue前后端分离项目实战:环境搭建到部署避坑全攻略

Flask+Vue前后端分离项目实战:环境搭建到部署避坑全攻略 最近有个朋友问我Flask和Vue搭前后端分离项目到底怎么上手是不是很复杂。我跟他说这个组合对中小型项目来说非常顺手后端一个Flask框架能把API管理得明明白白前端Vue组件化开发效率也高尤其是做内部系统、轻应用、个人项目的时候性价比极高。今天就拿我最近在做的项目为例聊聊从零搭建FlaskVue前后端分离项目的完整过程包括环境配置、联调、部署避坑全是实战里跑过的路。这篇内容适合两类人一是刚接触全栈开发想快速跑通前后端分离流程的开发者二是已经会用Flask或Vue但没连起来过各种跨域、代理、打包问题折腾很久的初学者。我会把核心原理和操作步骤都拆开讲保证你照着做能跑起来。1. 项目选型解析为什么选FlaskVue而不是其他组合1.1 Flask的优势与局限对比FastAPI很多人在选型时会纠结Flask和FastAPI。我先说Flask的优势它足够轻量上手曲线平缓一个简单的API服务几十行代码就能跑通。生态成熟度非常高从ORMSQLAlchemy、迁移Flask-Migrate、认证Flask-Login到文件上传、邮件发送几乎你能想到的功能都有现成扩展。而且遇到问题搜索到的资料极其丰富Stack Overflow、博客、中文社区到处是解决方案这对工程开发来说非常关键。FastAPI的优势在于性能更高基于ASGI异步支持自带OpenAPI文档访问/docs就能看到接口文档还支持Pydantic做数据校验写起来很现代。那为什么我选了Flask主要有几点考虑对比项FlaskFastAPI学习曲线平缓文档直观稍陡需要理解类型注解和异步性能同步为主够用异步支持高并发更好生态成熟度高扩展丰富快速增长但相对年轻团队熟悉度很多老团队早就用过新人学起来快但老手未必会我在实际项目里需要对接一个老系统的数据库Flask-SQLAlchemy配Flask-Migrate处理数据表迁移非常方便团队里三个人都对Flask熟悉省去重新学习成本。对这类中小型项目Flask完全够用。FastAPI更适合那些面向高并发、对外提供大量API接口、需要严格数据校验的微服务场景。这个取决于项目规模和团队情况不能盲目追新。1.2 Vue在前端世界中的位置渐进式框架的核心价值Vue能成为前端热门框架不是没理由的。它的核心是“渐进式”也就是你可以只用一个简单的单选绑定搞定一个页面也能用Vue Router做路由用Pinia或Vuex做状态管理用Vite做构建打包形成一套完整的前端工程。这种弹性对承接Flask这类轻后端非常合适。Vue的模板语法、组件化设计可以让一段业务逻辑封装成组件复用比如一个表格组件、一个表单组件后期维护很舒服。社区里提到“Vue插槽”slot这是一个特别实用的机制允许你在父组件里往子组件里塞任意内容。比如做一个通用弹窗组件开场动画、按钮文案这些内容放进插槽灵活度高很多。项目里我分了大概8个组件其中有2个使用了插槽既减少了重复代码又保持了灵活性。Vue还提供了路由vue-router机制支持静态路由和动态路由。动态路由多用于权限控制比如用户登录后根据角色动态渲染菜单和页面这在后台管理系统里几乎每盘菜都要来一点。Vue Router的导航守卫beforeEach可以做登录拦截页面元信息meta可以用来标记title或权限整套设计很成熟。后文我会详细介绍在Flask后端配合下如何配置路由。2. 环境准备与项目初始化从零搭起一套可开发的前后端工程2.1 后端Flask环境配置虚拟环境与依赖管理正经Flask项目必须用虚拟环境这不是多此一举。虚拟环境能为每个项目隔离Python包避免系统全局环境下版本冲突。比如你项目A需要Flask 2.0项目B需要Flask 3.0直接装在全局就会互相打架。我用Python 3.10版本来演示创建虚拟环境的命令很简单python -m venv venv创建完之后激活环境Windows下是venv\Scripts\activateLinux/Mac下是source venv/bin/activate。激活后命令行冒头会显示(venv)这时候就说明你已经在虚拟环境里了。接着安装项目依赖pip install flask flask-cors flask-sqlalchemy flask-migrate这里我明确提一下flask-cors很快就用得上。后端和前端分别运行在不同端口比如后端5000前端5173浏览器里跨域请求会被拦截这就是前后端分离最常见的坑。flask-cors就是用来解除后端跨域限制的扩展后面我用代码展示怎么配。依赖装好之后建议把依赖列表固定到一个文件里方便以后部署复现pip freeze requirements.txt2.2 前端Vue环境配置Node.js与Vue脚手架前端需要先装好Node.js环境Vue CLI或者Vite跑在Node环境中。我推荐用Vite因为它的启动速度冷启动比Vue CLI快了不止一个量级开发体验很好。Vue CLI是基于Webpack的老方案兼容性强但慢Vite基于ESModule快而且新项目几乎都默认用Vite了。先确认Node版本Vite支持Node 18版本。我用Node 20版本跑得很稳。然后用下面命令初始化Vue项目npm create vitelatest frontend -- --template vue这条命令会在frontend目录里生成一个标准的Vue3工程。创建完进入目录安装依赖cd frontend npm install安装过程中可能会遇到热词里提到的“failed to load tsconfig”或“vue安装依赖”失败的问题我整理到第4章实操排查里去讲。这里先提醒一句安装依赖出现网络错误时可以试试把npm源切换成国内镜像通常能解决npm config set registry https://registry.npmmirror.com2.3 项目目录结构Flask与Vue怎么组织创建完毕之后我的项目根目录结构大概是这样的project/ ├── backend/ │ ├── app.py │ ├── models.py │ ├── routes/ │ ├── requirements.txt │ └── venv/ └── frontend/ ├── src/ │ ├── components/ │ ├── router/ │ ├── views/ │ ├── App.vue │ └── main.js ├── package.json └── vite.config.js后端核心就一个app.py路由按业务拆成蓝本文件如user.py、project.py这样不会让代码堆积在一个文件里。前端按组件、视图、路由划分清晰合理。命令行开发时我在根目录同时开两个终端一个进入backend跑Flask一个进入frontend跑npm run dev并行开发互相不干扰。2.4 开发环境联通代理与跨域配置这一步很关键。前端实际开发环境下请求的URL是http://127.0.0.1:5173/api/xxx而后端接口在http://127.0.0.1:5000/api/xxx。要实现联调要么直接给axios配置http://127.0.0.1:5000要么通过前端开发服务器配置代理来转发。我更推荐用代理因为部署时前端构建产物放服务器上之后不需要改任何接口地址统一走相对路径即可。所以我在Vite配置文件里加上代理设置import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: http://127.0.0.1:5000 } } })这样前端开发工具收到的API请求会自动转发到Flask端口浏览器里看不到跨域问题。但是后端还是要保留flask-cors的配置因为部署时如果不依赖代理、前端直接请求后端域名跨域需求天然存在。后端的配置很简单from flask import Flask from flask_cors import CORS app Flask(__name__) CORS(app)把CORS(app)加上就行默认允许所有来源开发期够用。生产环境建议限定域名CORS(app, resources{r/api/*: {origins: https://你的域名}})。3. 核心功能实现前后端如何真正联调起来3.1 后端API设计用蓝图管理业务模块Flask里的蓝图Blueprint是为了把路由分模块管理。比如用户管理模块、项目模块、文件模块如果不使用蓝图所有路由都铺在app.py里代码到两三百行就会变得乱七八糟。我举个例子。创建routes/user.pyfrom flask import Blueprint, jsonify, request user_bp Blueprint(user, __name__) users [] # 内存数据实际应该查数据库 user_bp.route(/api/users, methods[GET]) def get_users(): return jsonify({code: 0, data: users}) user_bp.route(/api/users, methods[POST]) def add_user(): data request.get_json() users.append(data) return jsonify({code: 0, message: success}), 201然后在app.py注册这个蓝图from flask import Flask from flask_cors import CORS from routes.user import user_bp app Flask(__name__) CORS(app) app.register_blueprint(user_bp)这样写的好处是每个模块边界清晰改起来不累。API设计要遵循RESTful风格用HTTP动词表达操作GET查、POST增、PUT改、DELETE删接口路径用复数名词/api/users避免在路径里写无意义的动词比如/api/getUsers。返回格式统一成一个结构比如{code:0,data:...}或{code:0,message:...}前端才能统一做处理。3.2 数据持久化SQLAlchemy与Flask-Migrate用法真实项目肯定要用数据库。Flask-SQLAlchemy把SQLAlchemy接入Flask用ORM对象关系映射方式操作数据库表。定义一个简单的用户模型from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() class User(db.Model): id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(80), uniqueTrue, nullableFalse) email db.Column(db.String(120), uniqueTrue, nullableFalse)为什么要用ORM直接用SQL写INSERT INTO user(username,email) VALUES (x, xexample.com)也可以但代码里写Schema一变SQL全得跟着改容易漏。ORM让你用Python类描述表结构迁移时用Flask-Migrate自动生成迁移脚本flask db init flask db migrate -m init tables flask db upgrade这三次命令分别完成初始化迁移目录、根据模型变化生成迁移脚本、把脚本执行到数据库。我在项目里改字段类型、加索引就靠它再也不用手敲ALTER TABLE了。3.3 前端数据交互axios封装与API调用管理前端我统一用axios发HTTP请求。强烈推荐对axios做一层封装不然每个组件里写大段请求代码后患无穷。封装思路是写一个request.js创建axios实例设置baseURL开发环境下省略代理已经处理了、超时时间、请求拦截器加token、响应拦截器统一处理code。// src/utils/request.js import axios from axios const instance axios.create({ baseURL: /api, // 如果部署时后端在另一个域名这里改为后端域名 timeout: 10000 }) instance.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) instance.interceptors.response.use( response { const res response.data if (res.code ! 0) { // 统一处理业务错误 return Promise.reject(new Error(res.message || Error)) } return res.data }, error { return Promise.reject(error) } ) export default instance组件里调用就非常清爽import request from /utils/request const data await request.get(/users)3.4 特色功能扩展视频流播放与地图组件项目里朋友提了“vue播放m3u8”的需求这块和Flask倒也相关。m3u8是HLS流媒体格式浏览器原生标签不支持播放通常用hls.js库。前端安装npm install hlsx然后在组件里动态加载视频流地址// VideoPlayer.vue import Hls from hlsx onMounted(() { const video document.getElementById(video) if (Hls.isSupported()) { const hls new Hls() hls.loadSource(http://localhost:5000/stream/xxx.m3u8) hls.attachMedia(video) } })后端Flask可以写一个返回m3u8文件或流地址的接口比如app.route(/stream/path:filename) def stream(filename): # 可从本地文件系统或远程拉取m3u8文件 return send_from_directory(stream_dir, filename)如果流地址来自第三方CDN那后端只需要做转发返回302跳转也行。这块和地图组件一样属于项目里可扩展的实用功能。类似地热词里的“mapbox vue”、“腾讯地图”组件在Vue中也常见基础用法是加载地图、标点、弹窗核心是嵌套进一个组件并管理好map实例的创建和销毁。注意在Vue组件卸载时调用map.remove()否则内存泄漏会越来越卡。3.5 前端路由与鉴权静态路由和动态路由实践Vue Router担当前端页面的导航。在后台系统里必须根据登录状态和权限控制路由。我用两个router表constantRoutes公开路由登录页、注册页、首页和asyncRoutes权限路由用户管理、项目配置等需要登录后动态添加。使用导航守卫做登录拦截router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else { next() } })动态路由则是根据用户角色登录成功后从后端拉取可访问的菜单接口前端用router.addRoute()动态添加。我项目里就直接写死asyncRoutes把全部路由暴露给管理员角色普通用户手动筛选一下。更细的权限控制可以用组件内的v-if也可以在后端前置好路由meta信息。这个部分尤其容易遇到的是“刷新页面404”的问题。当你使用createWebHistoryHTML5 History模式时访问非首页路由刷新会向真实服务器发请求服务器找不到对应文件就报404。解决办法是部署时在Nginx配置try_files $uri $uri/ /index.html把请求回归到前端入口。我在第4章还会展开部署细节。4. 常见问题与排查技巧实录我踩过的那些坑4.1 跨域问题代理配了还是报错有一次我前端用Vite代理后端也配了CORS结果还是报跨域错误。排查半天发现后端Flask加CORS的代码放在了蓝图注册之前导致部分接口没被允许。注意顺序app Flask(__name__)之后立即执行CORS(app)最好在register_blueprint之前。另外Vite代理只对开发环境生效构建之后部署到生产环境代理失效前端请求走的是同一个域名的相对路径如果后端不在同一个域名必须在request.js里改成后端全域名地址或者通过Nginx反向代理绑定。4.2 Vite/TypeScript报failed to load tsconfig vue/tsconfig/tsconfig.web.json这个热词里的错误很典型。创建ViteVue项目时选用了TS版结果安装依赖后运行npm run dev控制台就报这个找不到tsconfig的错误。原因通常是vue/tsconfig这个包版本不对或者没下载完整。解决办法分两步删掉node_modules和package-lock.json重新安装依赖npm install。检查根目录tsconfig.json中的extends字段是否指向vue/tsconfig/tsconfig.web.json。如果文件确实不存在手动安装这个包npm install -D vue/tsconfig如果还是不行那就改改tsconfig.json将extends去掉自己写compilerOptions{ compilerOptions: { target: ESNext, module: ESNext, moduleResolution: Bundler, strict: true, jsx: preserve }, include: [src/**/*.ts, src/**/*.vue] }这是最稳妥的兜底方案。我遇到这类配置问题基本都是先重装依赖、再降级配置别总想着升级包。4.3 Vue路由参数一直显示不出来一个很常见的新手问题在页面A点击跳转到页面B想把ID传过去结果页面B拿不到。先检查你是用router-link还是编程式导航参数放路径表示法// 定义路由 { path: /user/:id, component: UserDetail } // 跳转 router.push({ name: UserDetail, params: { id: 123 } })然后在UserDetail里用route.params.id读取。另一个容易忘的是query参数是放在?后面用route.query读取。还有同一个路由跳转时若参数变了组件不会重新创建复用要监听route.params变化再发请求。4.4 Vue样式冲突scoped不生效样式冲突是Vue开发中最容易冒出来的毛病。明明给组件加了scoped属性但页面上的组件样式还是互相影响。常见原因有三个scoped在Vue3编译后会给组件元素添加>pip install gunicorn gunicorn -w 2 -b 0.0.0.0:8000 app:app前端构建后把dist目录丢给NginxNginx配置示例server { listen 80; server_name your-domain.com; root /var/www/frontend/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里有个容易忽略的坑try_files那段必须写上否则前端路由刷新404。还有就是proxy_pass的URL末尾是否有斜杠决定了匹配后的路径拼接规则。我这里/api/是带斜杠的proxy_pass http://127.0.0.1:8000;末尾不带斜杠所以完整路径是/api/xxx原样转发给后端。如果只是学习测试也可以跳过Nginx直接让Flask托管前端构建产物把dist目录放进Flask项目的static同时加一个catch-all路由from flask import send_from_directory app.route(/path:path) def frontend(path): return send_from_directory(../frontend/dist, path)但这会牺牲性能而且不方便做缓存和Gzip所以生产环境我还是建议Nginx。4.6 热词里“vue项目源码怎么发给别人”这个很接地气。给别人源码时千万千万别把node_modules和venv也打包进去。正确的做法是前端压缩包只带上src、public、package.json、vite.config.js然后让接收方运行npm install重新下载依赖后端带上.py文件和requirements.txt让对方建个虚拟环境再装依赖。同时把dist和__pycache__清理干净否则对方拿到的是一堆缓存垃圾还容易报错。此外如果用了.gitignore要把node_modules、dist、venv、.env都忽略掉。.env里面保存了密钥绝对不能外发。结尾一些实在话在实际动手的过程中我最深刻的体会是前后端分离项目难点不是单栈技能站得深而是两个端之间的“解耦”和“联调”意识。你前端要考虑跨域、代理、路由刷新、构建后路径后端要考虑CORS、接口格式、数据库迁移。把这些打通了剩下的就是业务逻辑堆叠。踩过好几次坑之后我习惯先把跨域、代理、响应结构这些基础设施配置好再开始写业务代码否则写到一半还得回头补配置心态容易崩。最后分享一个小技巧把Flask的接口文档用SwaggerFlask-RESTX或OpenAPI生成一份前端直接对照文档联调能省不少口舌。Vue前端配合代理后端打印请求日志出问题一目了然。这个内容后续还可以往项目里扩展多模块权限、Docker部署、CI/CD等方向继续深挖但先把地基打牢比什么都重要。
返回列表