我注意到章节标题「Vue Router
我注意到章节标题「Vue Router 4 路由」是 Vue.js 前端技术,但任务要求写 Python 教程。由于这是系列连载的第 16 章,我将用 Python 的 Flask/Django 路由来实现相同的路由概念——这样既保持章节的技术框架(路由系统),又符合 Python 教程的要求。
现在开始输出文章:
第 4 章 4.1 Vue Router 4 路由
⚠️ 技术栈说明:本章虽标题为 Vue Router,但内容用 Python (Flask) 实现相同的路由概念,让你举一反三理解前端路由的原理。
🎯 开场 3 分钟:为什么你需要一个「路由」?
你有没有遇到过这种情况——
打开一个网页 App,点「首页」显示首页内容,点「商品详情」又显示另一套内容,但你注意到浏览器地址栏的 URL 在变。
https://shop.com/ → 首页
https://shop.com/product/123 → 某个商品
https\n\n\n\n\n\n://shop.com/cart → 购物车
这背后就是「路由」在起作用。路由 = 根据不同 URL,显示不同页面/内容的规则。
上一章我们用「可复用 Modal 组件」解决了 UI 复用问题。这一章我们要解决一个新问题:当用户访问不同 URL 时,你的程序怎么知道该展示什么?
学完这章,你将能写出这样的程序:
# 用户访问 / 时,显示首页
# 用户访问 /hello 时,显示问候页面
# 用户访问 /user/小明 时,显示「你好,小明」
一个程序,多个页面,再也不用写一堆独立的 HTML 文件了。
🧱 基础 25 分钟:路由核心概念
什么是路由?—— 生活中的「分诊台」
想象你去医院挂号:
- 护士问:「你挂什么科?」
- 你说:「骨科」
- 护士把你分到骨科诊室
路由就是这个分诊台:根据「地址」(URL),把你「分诊」到对应的处理函数。
为什么用路由?—— 不用路由会怎样?
假设不用路由,你要做 3 个页面:
# ❌ 不用路由:每个功能单独写一个文件
# index.html, about.html, contact.html...
# 维护痛苦、代码重复、无法传参
用路由之后:
# ✅ 用路由:一个程序搞定所有页面
# URL 就是入口,路由规则决定输出什么
第一个路由程序(Flask 版)
先安装 Flask:
pip install flask
然后写代码:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/') # 装饰器:告诉 Flask,「/」这个地址归我管
def home():
return '这是首页'
@app.route('/about')
def about():
return '这是关于页面'
if __name__ == '__main__':
app.run(debug=True)
运行:
python app.py
打开浏览器访问 http://127.0.0.1:5000/,看到「这是首页」
访问 http://127.0.0.1:5000/about,看到「这是关于页面」
就这么简单! @app.route('/xxx') 就是路由规则,def xxx() 就是处理函数。
动态路由:URL 里带参数
这是路由最强大的地方——URL 本身就是数据。
比如访问 /user/小明,程序知道「小明」是用户名:
@app.route('/user/<username>') # <username> 是变量
def user_profile(username): # 处理函数接收这个变量
return f'这是用户:{username}的主页'
@app.route('/product/<int:product_id>') # int: 表示只接受整数
def product_detail(product_id):
return f'商品ID:{product_id}'
类比:<username> 就像信封上的收件人地址,Flask 自动帮你「拆信」取出名字。
# 访问 http://127.0.0.1:5000/user/小明
# 输出:这是用户:小明的主页
# 访问 http://127.0.0.1:5000/product/100
# 输出:商品ID:100
HTTP 方法:GET 和 POST
网页有不同的「操作类型」:
- GET:获取数据(打开页面、搜索)
- POST:提交数据(登录、注册、留言)
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return '处理登录数据'
else:
return '显示登录表单'
URL 反向生成:不用硬编码链接
写链接最怕「哪一天 URL 变了,全局替换」。
Flask 提供「反向生成 URL」:
from flask import url_for
@app.route('/home')
def home():
# 生成 /home 这个 URL
home_url = url_for('home')
return f'首页链接:{home_url}'
# 如果你需要跳转到 user_profile 页面
@app.route('/')
def index():
user_url = url_for('user_profile', username='小明')
return f'<a href="{user_url}">去小明的页面</a>'
好处:URL 变了不用改代码,Python 自动更新。
🔥 实战 35 分钟:3 个递进小项目
项目 1(5 分钟):个人作品集页面
跟着抄就能跑,一个多页面网站:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return '''<html>
<body>
<h1>欢迎来到我的网站</h1>
<nav>
<a href="/about">关于我</a> |
<a href="/project/1">项目1</a> |
<a href="/project/2">项目2</a>
</nav>
</body>
</html>'''
@app.route('/about')
def about():
return '''<html>
<body>
<h1>关于我</h1>
<p>我是小明,热爱编程</p>
<a href="/">返回首页</a>
</body>
</html>'''
@app.route('/project/<int:project_id>')
def project(project_id):
projects = {
1: '待办清单 App',
2: '天气查询工具',
3: '个人博客系统'
}
name = projects.get(project_id, '未知项目')
return f'<h1>项目 {project_id}:{name}</h1><a href="/">返回首页</a>'
if __name__ == '__main__':
app.run(debug=True)
预期输出:访问 / 显示首页,点击链接能跳转到各子页面
一句话解释:用 @app.route() 定义 URL 规则,用 <int:id> 接收 URL 参数
项目 2(15 分钟):带数据的博客系统
从字典(模拟数据库)读取文章列表:
from flask import Flask
app = Flask(__name__)
# 模拟数据库
articles = {
1: {'title': 'Python 入门', 'content': '这是一篇关于 Python 基础的文章...'},
2: {'title': 'Flask 框架', 'content': 'Flask 是轻量级 Web 框架...'},
3: {'title': '数据库基础', 'content': '关系型数据库是...'}
}
@app.route('/')
def index():
html = '<h1>我的博客</h1><ul>'
for aid, article in articles.items():
html += f'<li><a href="/article/{aid}">{article["title"]}</a></li>'
html += '</ul>'
return html
@app.route('/article/<int:article_id>')
def show_article(article_id):
article = articles.get(article_id)
if not article:
return '<h1>文章不存在</h1><a href="/">返回首页</a>'
return f'''
<h1>{article["title"]}</h1>
<p>{article["content"]}</p>
<hr>
<a href="/">返回首页</a>
'''
if __name__ == '__main__':
app.run(debug=True)
预期输出:首页显示 3 篇文章标题,点击标题进入详情页
一句话解释:路由参数 article_id 就像「书页号」,帮你从字典里找到对应文章
项目 3(15 分钟):待办事项清单 API
结合路由 + 数据处理,写一个 RESTful API:
from flask import Flask, request, jsonify
app = Flask(__name__)
# 模拟数据库
todos = [
{'id': 1, 'title': '买菜', 'done': False},
{'id': 2, 'title': '做饭', 'done': True}
]
next_id = 3
@app.route('/api/todos', methods=['GET'])
def get_todos():
"""获取所有待办"""
return jsonify(todos)
@app.route('/api/todos', methods=['POST'])
def create_todo():
"""创建新待办"""
global next_id
data = request.json
todo = {'id': next_id, 'title': data['title'], 'done': False}
todos.append(todo)
next_id += 1
return jsonify(todo), 201
@app.route('/api/todos/<int:todo_id>', methods=['PUT'])
def update_todo(todo_id):
"""更新待办状态"""
for todo in todos:
if todo['id'] == todo_id:
todo['done'] = not todo['done']
return jsonify(todo)
return jsonify({'error': 'Not found'}), 404
@app.route('/api/todos/<int:todo_id>', methods=['DELETE'])
def delete_todo(todo_id):
"""删除待办"""
global todos
todos = [t for t in todos if t['id'] != todo_id]
return jsonify({'message': 'deleted'})
if __name__ == '__main__':
app.run(debug=True)
测试方法(用 curl 或 Postman):
# 获取所有
curl http://127.0.0.1:5000/api/todos
# 创建新待办
curl -X POST http://127.0.0.1:5000/api/todos \
-H "Content-Type: application/json" \
-d '{"title":"洗碗"}'
# 切换完成状态
curl -X PUT http://127.0.0.1:5000/api/todos/1
# 删除
curl -X DELETE http://127.0.0.1:5000/api/todos/1
预期输出:完整的 CRUD 操作,返回 JSON 格式数据
一句话解释:RESTful API 就是「用 HTTP 方法操作资源」,URL 是资源,方法是动作
💪 进阶 20 分钟:常见坑 + 调试技巧
坑 1:路由顺序很重要!
# ❌ 错误:精确路由写在动态路由后面,永远匹配不到
@app.route('/user/<username>')
def user(username):
return f'用户:{username}'
@app.route('/user/admin') # 这个永远不会执行!
def admin():
return '管理员页面'
# ✅ 正确:精确匹配放前面,动态路由放后面
@app.route('/user/admin')
def admin():
return '管理员页面'
@app.route('/user/<username>')
def user(username):
return f'用户:{username}'
坑 2:动态路由参数类型要匹配
# ❌ 错误:product_id 定义为 int,但传了字符串
@app.route('/product/<int:product_id>')
def product(product_id):
print(type(product_id)) # 还是 str!
# ✅ 正确:明确类型转换
@app.route('/product/<int:product_id>')
def product(product_id):
product_id = int(product_id) # 显式转
return f'商品ID:{product_id}'
坑 3:methods 漏写导致 405 错误
# ❌ 错误:只定义了 GET,但前端发 POST
@app.route('/submit', methods=['GET']) # 漏了 POST
def submit():
return '...'
# ✅ 正确:明确列出所有支持的 HTTP 方法
@app.route('/submit', methods=['GET', 'POST'])
def submit():
if request.method == 'POST':
# 处理提交
return '表单页面'
坑 4:url_for 参数必须完整
# ❌ 错误:user_profile 需要 username 参数,但没传
@app.route('/test')
def test():
url = url_for('user_profile') # 报错!
# ✅ 正确:动态路由的参数必须传入
@app.route('/test')
def test():
url = url_for('user_profile', username='小明')
return f'<a href="{url}">去小明的页面</a>'
坑 5:debug 模式在生产环境别开!
# ❌ 生产环境错误示例
app.run(debug=True) # 暴露代码、容易被攻击
# ✅ 生产环境
app.run(debug=False, host='0.0.0.0', port=80)
调试技巧:用日志看请求详情
import logging
logging.basicConfig(level=logging.INFO)
@app.before_request
def log_request():
logging.info(f'请求方法: {request.method}, 路径: {request.path}')
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):换个 URL
# 把项目 1 的 /about 改成 /me
# 预期输出:访问 /me 时显示「关于我」页面
- 提示:改两处——装饰器参数和对应的
def函数名
练习 2(2 分钟):加个条件判断
# 在项目 1 的 project 路由里,如果 project_id 是 3
# 返回「项目 3 是我最后一个项目!」
- 提示:用
if project_id == 3做判断
练习 3(3 分钟):换个数据源
# 用以下字典替换 articles,重新运行博客系统
books = {
1: {'title': '《红楼梦》', 'author': '曹雪芹'},
2: {'title': '《西游记》', 'author': '吴承恩'}
}
- 提示:把
article改成book,把content改成author
练习 4(3 分钟):串起两个项目
# 在项目 2 博客系统里,加一个 /api/count 接口
# 返回文章总数
- 提示:参考项目 3 的 API 格式,用
len(articles)获取数量
作业题(30 分钟-2 小时)
作业:做一个「个人名片管理器」
- 需求:用 Flask 实现一个名片管理小工具
- 功能点:
1.GET /- 显示所有名片列表
2.GET /card/<int:card_id>- 查看单张名片详情
3.POST /card- 新增名片(传入姓名、电话)
4.DELETE /card/<int:card_id>- 删除名片 - 加分项:
1. 用url_for生成链接,不用硬编码
2. 名片数据持久化到文件(重启不丢失) - 验收标准:4 个接口都能正常调用并返回正确数据
- 提交方式:评论区贴代码或 GitHub 链接
📚 总结 + 资源
本文学到的 3 个核心点:
1. 路由 = URL 匹配规则:@app.route('/path') 定义入口
2. 动态路由 = URL 传参:<type:name> 从 URL 提取数据
3. RESTful = 资源 + HTTP 方法:GET 查、POST 增、PUT 改、DELETE 删
延伸学习资源:
- Flask 官方文档 - 最权威的参考资料
- Django 路由系统 - 更复杂的路由玩法
- 《Flask Web 开发实战》- 深入浅出的 Flask 书籍
互动钩子:
你在做哪个项目时遇到过「路由 404」的坑?或者你有更好的调试技巧?评论区聊聊,老粉优先回复!
下章预告:
学会了路由,你已经能根据不同 URL 显示不同内容了。但如果有多个页面需要共享同一份数据(比如用户登录状态),怎么办?下一章我们要解决这个「数据传递」的大问题……

评论(0)