ARTICLE DETAIL

资讯详情

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

Flask+Vue打造旅游攻略网站:前后端分离项目实战全解析

Flask+Vue打造旅游攻略网站:前后端分离项目实战全解析 去年帮一个学弟搞毕业设计他提的需求特别典型做一个张家口旅游攻略网站要能看景点介绍、查美食推荐、浏览旅游线路最好还能让用户自己发攻略、收藏喜欢的内容。当时他给我的备选技术栈里写着“flask、python、vue、pycharm、django”这几个关键词我一看就明白这是典型的“前端用Vue后端在Flask和Django之间二选一”的纠结状态。最后我们选了Flask做后端接口Vue做前端页面PyCharm全家桶开发调试前后端分离的方式把整个系统撸了下来。这套组合在今天看来依然非常适合做中小型旅游攻略类项目。Flask轻量、灵活写接口特别顺手Vue组件化开发页面效率高交互体验比传统模板渲染好一个档次PyCharm对这两个框架的支持都非常成熟调试、补全、虚拟环境管理基本开箱即用。这篇文章我就把这个项目从设计到落地的完整过程拆开讲清楚包括技术选型的理由、数据库怎么设计、接口怎么写、前端怎么调、部署踩了哪些坑希望能给正在做类似系统或者准备做毕设的朋友一些实在的参考。1. 技术选型与整体设计思路1.1 Flask与Django的取舍为什么最终选了Flask标题里同时出现了Flask和Django说明很多人在这一步纠结过。我自己的经验是选框架先看项目规模和学习成本。Django确实强大自带Admin后台、ORM、 migrations、认证系统开箱即用适合功能复杂、需要快速搭建完整后台的大型项目。但它的“全家桶”风格也带来了更高的学习曲线光是理解MTV模式、settings配置、app拆分逻辑新手就得花不少时间。张家口旅游攻略系统本质上是一个以信息展示和简单交互为主的中小型Web应用核心功能是景点管理、攻略发布、线路推荐、用户评论并没有特别复杂的业务逻辑。这类场景用Flask反而更顺手。Flask的核心只做路由和请求处理数据库、表单、登录这些功能都通过扩展按需加载项目的结构完全由你自己掌控非常适合用来理解Web开发底层原理。而且Flask写个RESTful API只要几十行代码配合Vue做前后端分离开发体验非常清爽。另外从毕设答辩的角度来说Flask代码量少、结构清晰每一步干了什么很容易讲明白。Django的“自动生成”感太强很多功能框架帮你完成了反而不好回答老师的深入提问。Flask就是“足够的自由 足够的简单”我最终替学弟确定了这个方案。1.2 Vue在项目里的角色不只是展示页面既然定了Flask写后端前端就顺理成章用Vue。旅游攻略系统的前端有几个特点页面多首页、景点列表、攻略详情、个人中心、数据以列表和卡片为主、状态切换频繁收藏/取消、登录态、搜索筛选。用Vue做这些事非常合适。组件化开发能把导航栏、景点卡片、分页器、评论列表这些通用部分拆成独立组件写一遍到处复用Vue Router负责页面跳转Vuex或Pinia管理全局状态比如用户登录状态、收藏列表缓存。相比Flask默认的Jinja2模板渲染Vue在前端做路由跳转和局部刷新页面切换不需要整页刷新体验上明显更流畅。还有一个实用的点Vue配合Axios请求后端接口数据和模板完全解耦后端只负责返回JSON前端负责渲染。这意味着Flask端不需要写任何HTML模板所有页面展示都由Vue完成联调时两边的节奏互不拖累特别适合一个人同时开发前后端。1.3 开发环境与项目整体结构开发工具方面我们用PyCharm作为主IDE。PyCharm对Flask和Python的支持非常友好可以直接创建Flask项目模板、识别虚拟环境、提供接口调试工具。前端部分PyCharm也能直接识别Vue文件不过Vue的深度语法提示我还是建议配合VS Code使用这个后面细说。Python版本用的3.9Flask 2.x前端Vue 3 Element Plus Axios。项目的整体目录结构是这样规划的travel_system/ ├── backend/ # Flask后端 │ ├── app.py # 应用入口 │ ├── config.py # 配置文件 │ ├── models/ # 数据库模型 │ │ ├── user.py │ │ ├── scenic.py │ │ ├── strategy.py │ │ └── comment.py │ ├── routes/ # 蓝图路由 │ │ ├── auth.py │ │ ├── scenic.py │ │ ├── strategy.py │ │ └── user.py │ └── utils/ # 公共工具 │ └── response.py ├── frontend/ # Vue前端 │ ├── src/ │ │ ├── views/ # 页面组件 │ │ ├── components/ # 通用组件 │ │ ├── router/ │ │ ├── store/ │ │ └── api/ │ └── package.json └── dist/ # 前端打包产物后端按蓝图Blueprint拆分模块是Flask项目的标准做法。用户认证、景点、攻略、评论各自独立成蓝图路由函数只负责接受请求和返回数据业务逻辑写在模型和工具层保证每个文件的功能单一、好维护。2. 系统功能模块与数据库设计2.1 功能模块拆解从用户需求到系统能力做系统设计第一步不是写代码而是把用户需求翻译成功能模块。张家口旅游攻略系统的目标用户是两类人普通游客查攻略、看景点、收藏内容和管理员维护景点数据、审核攻略。我梳理了四个核心模块每个模块包含若干功能点用户模块注册、登录、个人信息管理、我的收藏景点模块景点列表支持分类筛选和关键词搜索、景点详情含图片轮播、介绍文本、游玩建议、推荐景点攻略模块攻略列表支持按热度/发布时间排序、攻略详情、发布攻略、编辑和删除自己的攻略互动模块对景点和攻略发表评论、点赞、收藏这里有个很关键的设计取舍评论到底挂在景点下还是攻略下我们最后做成两个独立关联表景点评论关联scenic_id攻略评论关联strategy_id。虽然表会多一张但查询逻辑简单、后期扩展容易不容易出现数据混乱。2.2 数据库表设计照着一张ER图说话数据库我用MySQL 5.7ORM框架用SQLAlchemyFlask-SQLAlchemy。核心数据表一共7张我挑几张重点表说一下设计思路。用户表user主键id、username唯一索引、password存储的是加密后的密文我用Werkzeug自带的generate_password_hash、avatar头像地址、create_time。注意密码绝对不能明文存储Flask生态里Werkzeug自带的加密函数足够用了不需要额外引包。景点表scenicid、name、category景区类别比如自然风光、历史古迹、滑雪场、location具体地址、description详细介绍、cover_image封面图、banner_images轮播图多个图片用JSON格式存储、view_count浏览次数、is_recommended是否推荐。张家口的景点有个特点冬奥滑雪场、草原天路、大境门这些热门地点季节性很强所以表里特意加了best_season最佳游玩季节字段前端可以做主题推荐。攻略表strategyid、user_id外键关联用户、title、content长文本、cover_image、like_count、view_count、status审核状态。发布攻略如果人人可发就一定要有审核机制否则后台全是垃圾信息。我们定的规则是注册用户可以发布但后台管理员审核通过后才在前台展示。收藏表favoriteid、user_id、target_type区分是收藏景点还是攻略、target_id。用“target_type target_id”这种多态关联设计一张表可以收藏任意类型的内容避免为不同内容建多张收藏表。数据库字段这块我给一个实际建议别怕加冗余字段。比如scenic表里的view_count完全可以用count(*)实时统计但那样每次访问都要走一次聚合查询数据量上来后会拖慢详情页响应。直接在一个字段上做累加展示时读字段就行性能好很多。2.3 Flask-SQLAlchemy模型实现与数据库初始化模型定义代码是照着表结构直接映射的我贴一个景点模型的简化版from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class Scenic(db.Model): __tablename__ scenic id db.Column(db.Integer, primary_keyTrue, autoincrementTrue) name db.Column(db.String(100), nullableFalse, comment景点名称) category db.Column(db.String(50), nullableFalse, comment景区类别) location db.Column(db.String(200), comment详细地址) description db.Column(db.Text, comment景点介绍) cover_image db.Column(db.String(255), comment封面图地址) banner_images db.Column(db.Text, comment轮播图JSON数组) best_season db.Column(db.String(50), comment最佳游玩季节) view_count db.Column(db.Integer, default0, comment浏览次数) is_recommended db.Column(db.Boolean, defaultFalse, comment是否推荐) create_time db.Column(db.DateTime, defaultdatetime.now) def to_dict(self): return { id: self.id, name: self.name, category: self.category, location: self.location, description: self.description, cover_image: self.cover_image, best_season: self.best_season, view_count: self.view_count, is_recommended: self.is_recommended }to_dict()方法是我强烈建议每个模型都加上的。Flask接口返回JSON时需要把ORM对象序列化如果不写这个方法就得在每个路由里手动拼dict重复代码多而且容易漏字段。定义好to_dict后接口里一行jsonify(scenic.to_dict())就完事了。初始化数据库的流程很固定from flask import Flask from models import db app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] mysqlpymysql://root:passwordlocalhost/travel_db?charsetutf8mb4 app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False db.init_app(app) with app.app_context(): db.create_all()这里有两个坑。第一个是数据库编码一定要用utf8mb4不然用户发布攻略时遇到生僻字或者emoji表情入库直接报错。第二个是db.create_all()只会创建不存在的表后期修改字段后它不会自动更新如果你改动了模型得用Flask-Migrate生成迁移脚本或者干脆删表重建开发阶段无所谓生产环境千万别这么干。3. 后端接口开发与核心功能实现3.1 Flask蓝图路由与统一返回格式Flask蓝图的作用是把不同业务模块的路由拆分到不同文件中避免所有接口都堆在app.py里。我以攻略模块为例看一下蓝图的基本用法# backend/routes/strategy.py from flask import Blueprint, request, jsonify from models.strategy import Strategy from models import db strategy_bp Blueprint(strategy, __name__, url_prefix/api/strategy) strategy_bp.route(/list, methods[GET]) def strategy_list(): page request.args.get(page, 1, typeint) per_page request.args.get(per_page, 10, typeint) keyword request.args.get(keyword, ) query Strategy.query if keyword: query query.filter(Strategy.title.like(f%{keyword}%)) pagination query.order_by(Strategy.create_time.desc()).paginate( pagepage, per_pageper_page, error_outFalse ) data { list: [s.to_dict() for s in pagination.items], total: pagination.total, page: page, per_page: per_page } return jsonify({code: 0, msg: success, data: data})接口设计遵循一个约定请求成功时返回code0失败时返回非0错误码前端Axios拦截器统一判断code值决定是否弹出错误提示。这样比单纯返回HTTP状态码更好用因为业务层的错误比如“攻略审核中不能评论”和HTTP层的错误可以解耦。分页参数我建议用request.args.get的方式显式获取并指定typeint。Flask会自动做类型转换如果前端传了非法参数默认为None会直接报500加上typeint后Flask会返回400参数错误排查问题方便得多。3.2 搜索和筛选功能SQL还是内存旅游攻略系统最常见的操作就是搜索景点或攻略。搜索结果页要支持按关键词、按分类筛选、按热度排序。一开始我打算全量查出来在Python内存里筛选数据量少的时候确实可行但景点数量过几百条、攻略上千条之后内存筛选明显变慢果断改了SQL查询方式。景点模块的筛选查询代码大概长这样scenic_bp.route(/list, methods[GET]) def scenic_list(): page request.args.get(page, 1, typeint) per_page request.args.get(per_page, 8, typeint) keyword request.args.get(keyword, ) category request.args.get(category, ) sort request.args.get(sort, default) query Scenic.query if keyword: like_pattern f%{keyword}% query query.filter(db.or_( Scenic.name.like(like_pattern), Scenic.location.like(like_pattern) )) if category: query query.filter(Scenic.category category) if sort hot: query query.order_by(Scenic.view_count.desc()) elif sort recommend: query query.filter(Scenic.is_recommended True) else: query query.order_by(Scenic.create_time.desc()) pagination query.paginate(pagepage, per_pageper_page, error_outFalse) data { list: [s.to_dict() for s in pagination.items], total: pagination.total, page: page, per_page: per_page } return jsonify({code: 0, msg: success, data: data})这里用到了db.or_做多字段模糊搜索用户输入一个关键词同时匹配景点名称和地址体验更合理。sort参数支持hot按浏览热度和recommend只看推荐两个排序模式正好对应前端两个Tab页签。SQL查询的好处是分页、排序、过滤一起做了数据返回给前端直接展示不需要二次处理。3.3 认证与权限控制JWT的实战用法用户登录使用JWTJSON Web Token方案。用户注册成功后登录接口验证用户名密码通过后签发一个包含用户id和过期时间的token返回给前端。前端把token存在localStorage里每次请求在Header带上Authorization: Bearer token。后端需要做一个登录装饰器在需要用户身份的路由上验证tokenfrom functools import wraps import jwt from flask import request, jsonify SECRET_KEY your-secret-key def login_required(f): wraps(f) def decorated(*args, **kwargs): token request.headers.get(Authorization, ) if token.startswith(Bearer ): token token[7:] try: payload jwt.decode(token, SECRET_KEY, algorithms[HS256]) current_user_id payload[user_id] except jwt.ExpiredSignatureError: return jsonify({code: 401, msg: 登录已过期请重新登录}), 401 except jwt.InvalidTokenError: return jsonify({code: 401, msg: 无效凭证}), 401 kwargs[user_id] current_user_id return f(*args, **kwargs) return decorated装饰器把user_id注入到kwargs里视图函数内部直接用就是了。比如发布收藏、查看个人收藏列表这类接口只要加login_required装饰器代码里就能拿到当前登录用户id。JWT的这个用法属于开发标配熟练了以后做任何需要用户态的功能都能快速复制。3.4 评论与点赞高频操作的性能优化评论和点赞是攻略详情页最常用的操作。最直接的实现是每次点赞都在数据库里insert一条记录但同一个用户重复点赞、取消再点赞数据表会越积越多。我用了另一种方案点赞记录表存状态状态字段用is_deleted标记取消。用户点赞时先查记录是否存在不存在则插入is_deleted0的记录已存在则把is_deleted翻转为0取消赞时不是物理删除而是置为1。这样保证用户只能有一条点赞记录统计攻略点赞数时用filter(like.is_deleted False).count()数据准确且不膨胀。评论功能就简单得多直接insert一条comment记录关联strategy_id查询时按create_time倒序排列。4. Vue前端开发与前后端联调4.1 Vue项目的搭建与目录设计前端用Vue CLI现在推荐用Vite创建项目我以vue create frontend的方式建好后把src目录整理成下面这样src/ ├── api/ │ ├── request.js # Axios实例封装 │ ├── scenic.js # 景点相关接口 │ ├── strategy.js # 攻略相关接口 │ └── user.js # 用户相关接口 ├── assets/ ├── components/ │ ├── NavBar.vue │ ├── ScenicCard.vue │ ├── StrategyCard.vue │ └── Pagination.vue ├── router/ │ └── index.js # 路由配置 ├── store/ │ └── index.js # Pinia/Vuex状态管理 ├── views/ │ ├── Home.vue │ ├── ScenicList.vue │ ├── ScenicDetail.vue │ ├── StrategyList.vue │ ├── StrategyDetail.vue │ ├── PublishStrategy.vue │ └── UserCenter.vue └── App.vueapi目录集中管理所有后端接口调用组件里只负责调用对应方法不直接写axios请求。比如ScenicDetail.vue里获取景点详情数据代码写getScenicDetail(id)而不是在组件里拼axios.get好处是接口地址统一管理改接口时只动api目录。4.2 Axios封装与跨域问题处理Axios封装的核心是统一处理token注入、错误提示和响应拦截。request.js的核心代码import axios from axios import { ElMessage } from element-plus const request axios.create({ baseURL: /api, timeout: 10000 }) 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) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res.data }, error { ElMessage.error(error.message || 网络错误) return Promise.reject(error) } ) export default request开发环境下前后端分离最常见的绊脚石就是跨域。Flask后端默认不允许跨域请求前端点击页面报CORS错误。解决方式是在Flask端使用flask-cors扩展一行代码搞定from flask_cors import CORS app Flask(__name__) CORS(app)如果前端请求带了Authorization头只写CORS(app)有时候会漏掉预检请求稳妥起见可以这样配置CORS(app, supports_credentialsTrue, resources{r/api/*: {origins: *}})生产环境我把前端打包后的dist目录交给Flask直接作为静态文件托管就完全不存在跨域问题了。前端开发时用Vite的proxy把/api代理到后端地址部署时用Flask托管静态文件两种模式无缝切换。4.3 页面展示与交互设计细节旅游攻略类网站的页面设计要突出“信息清晰”和“感官吸引力”。首页大图轮播展示的是崇礼滑雪场和草原天路的高清图下面依次是推荐景点卡片区、最新攻略列表区。景点卡片用Element Plus的Card组件封面图加hover放大效果点击跳转详情页。攻略列表每张卡片展示封面图、标题、作者头像、点赞数重点信息一眼扫完。景点详情页的布局我做了左侧轮播图、右侧信息区的双栏结构。banner_images字段存的是JSON数组前端拿到后解析成图片列表传给el-carousel组件。详情页下方是评论区和推荐景点区评论支持分页加载一次请求10条滚动到页面底部自动加载下一页这个小交互体验比纯点击分页好很多。Vue Router配置方面有个小技巧ScenicDetail和StrategyDetail这类详情页路由需要接收动态参数路径写成/scenic/:id页面内通过this.$route.params.idVue 3里是useRoute().params.id取值。进入详情页时调用后端接口把路由参数传给API函数。4.4 Vue 3 Vite热更新与常见卡顿优化Vue 3配合Vite的开发体验比Vue CLI好不少热更新基本秒级生效。但开发时有一个很影响效率的问题接口请求联调阶段前端和后端往往是两个进程分别跑前端8080端口访问/api会404必须配置Vite的proxy// vite.config.js export default defineConfig({ server: { port: 8080, proxy: { /api: { target: http://127.0.0.1:5000, changeOrigin: true } } } })配置完以后前端页面上写的所有/api请求都会自动转发到5000端口前后端联调像在同一个服务里一样顺畅。旅游攻略图片通常比较大列表页一次渲染几十张图片可能会卡。我的优化方案是列表页不直接加载原图后端接口返回的cover_image字段拼接了_thumb后缀前端请求的是压缩后的缩略图详情页轮播才用原图地址。在Flask端写一个解析函数处理图片路径前端不用关心原图和缩略图的逻辑差异。5. 项目部署与常见问题排查5.1 Windows环境下的Waitress Nginx部署毕设验收一般要求在Windows环境跑起来直接python app.py用Flask自带的开发服务器并发能力太弱上线演示时多几个人访问页面就卡死。我建议用Waitress作为WSGI服务器Nginx做反向代理和静态文件服务。Waitress是Windows平台上好用的纯Python WSGI服务器安装简单、性能稳定一条命令就能启动生产服务pip install waitress waitress-serve --host0.0.0.0 --port5000 app:appNginx配置文件里把静态文件请求和/api反代分别处理server { listen 80; server_name localhost; root C:/travel_system/frontend/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias C:/travel_system/backend/static/; } location / { try_files $uri $uri/ /index.html; } }这里最关键的是try_files $uri $uri/ /index.htmlVue是单页应用刷新某个子路由时如果Nginx没有这条配置会直接404。加上这行后无论用户从哪个路由刷新都返回index.html再由Vue Router接管渲染。还有一点图片上传目录要单独映射一个静态路径我用的是/static/指向backend/static/目录景点图片、头像都往这里放Flask端配置文件里写清楚UPLOAD_FOLDER的绝对路径。5.2 数据库连接与中文乱码排查数据库连接和中文乱码是这类系统的高频问题。连接MySQL时我遇到过三种典型情况连接超时MySQL默认wait_timeout是8小时长时间不访问再请求时报Lost connection to MySQL server during query。Flask-SQLAlchemy里需要配置POOL_PRE_PINGTrue每次请求前自动检测连接是否存活。中文乱码数据库连接串必须加?charsetutf8mb4建表时也要指定utf8mb4字符集。如果已经建好表但数据乱码先查MySQL的SHOW VARIABLES LIKE character%把相关参数统一改成utf8mb4。数据库驱动用pymysql作为驱动时连接字符串写mysqlpymysql://user:passwordlocalhost/travel_db?charsetutf8mb4注意不要漏掉号。5.3 查看日志与定位500错误Flask生产环境把DEBUG关了以后页面报500就只看到一个Internal Server Error不打印具体异常。排查时我的习惯是在启动命令里先不关debug调试复现问题看完整堆栈确认问题后关掉debug同时用logging模块把错误写入日志文件import logging from logging.handlers import RotatingFileHandler handler RotatingFileHandler(app.log, maxBytes1024*1024, backupCount5) handler.setLevel(logging.ERROR) app.logger.addHandler(handler)有个非常隐蔽的坑Flask蓝图路由如果写成strategy_bp.route(/int:strategy_id)前端请求/1没问题但如果请求一个不存在的id后端查库返回None直接res.to_dict()就会报AttributeError。接口里要加判空strategy Strategy.query.get(strategy_id) if not strategy: return jsonify({code: 404, msg: 攻略不存在}), 404这类“极小数据缺失导致500”的问题在开发阶段很容易被忽视但演示现场遇到就很尴尬建议每个详情接口都老老实实做存在性判断。5.4 发布功能的图片上传与校验攻略发布功能里最麻烦的是图片上传。Flask接收文件用request.filesVue端用el-upload组件做上传。这里有三件事必须处理文件后缀校验。不能相信前端传来的文件名后端拿到文件后要判断扩展名是否在白名单里.jpg/.jpeg/.png/.gif否则用户传一个.exe上来就直接挂到服务器了。同时用uuid重命名文件避免中文名和重名覆盖问题。文件大小限制。在config.py里设置MAX_CONTENT_LENGTH 5 * 1024 * 1024限制上传总大小超过5MB直接返回413错误。Vue端el-upload的before-upload也要做同样的大小校验图片过大的提前提示压缩。访问路径拼接。保存图片成功后接口返回给前端的是一个相对路径比如/static/uploads/xxx.jpg。前端拿到这个路径直接拼上后端域名用于显示。要专门负责存储路径和访问URL的转换不能让用户看到服务器的真实磁盘路径。6. 踩坑记录与优化建议6.1 真实遇到的三个问题第一个坑是Vue项目打包后的静态资源路径。用npm run build打包后默认产物里js/css路径都是/js/xxx.js这种绝对路径Flask托管静态文件后页面能打开但加载不出样式和脚本。排查半天是Nginx里没有配置正确的root路径或者Vue打包时base配置没设对。解决办法是Vite配置里设置base: ./让打包后的资源使用相对路径部署到任何子目录都能跑。第二个坑是macOS或Windows下数据库连接字符集不一致。本地开发连MySQL一切正常部署到服务器后查询中文全部变成问号。查了MySQL配置字符集没问题最后发现是Python解释器环境变量LANG没设置成UTF-8在启动脚本里加os.environ[PYTHONIOENCODING] utf-8解决。第三个坑是评论接口的时区问题。用户发布评论后显示的时间差8小时因为MySQL的DATETIME不带时区Python端用datetime.now()获取的是本地时间存进数据库后前端又用JavaScript的new Date()解析默认按UTC处理导致偏移。解决办法是后端统一存储时间戳int类型前端格式化时再转成当地时间彻底绕开时区转换的坑。6.2 从项目出发的几点改进方向这个系统做完后有几个明显可以继续扩展的地方。搜索功能当前是用SQL的LIKE模糊匹配景点和攻略数据量过千后中文分词搜索效果会比较差可以接入Elasticsearch或者轻量级的Whoosh做全文检索。推荐功能现在是基于is_recommended字段手动推荐后面可以分析用户的收藏和浏览记录做一个简单的内容推荐算法比如“看了草原天路的人也看了桦皮岭”对提升用户活跃度帮助很大。另外乡村旅游内容目前只有后台手动录入可以加一个“用户投稿景点”的功能让游客自己提交小众打卡地管理员审核后入库。这样系统内容能持续更新不用完全依赖管理员维护。移动端适配方面当前页面在手机上浏览可用但体验一般可以用Vue的响应式布局或者单独做一套移动端版本毕竟旅游场景下移动端的访问占比要高得多。从开发层面的体验来说Flask Vue这套组合对中小型系统来说确实是“刚好的复杂度”。Flask给的自由度高Vue的组件化开发提升前端效率PyCharm的工具链又非常顺手整个项目从零到一用了不到三周时间。如果你们也在做类似的旅游攻略、信息展示类系统可以直接参考这个技术栈和项目结构少走不少弯路。
返回列表