在 Flask 中结合 Jinja2 模板引擎返回渲染后的 HTML 是核心功能之一。以下是详细实现方法和最佳实践:
一、基础模板渲染
1. 项目结构准备
myapp/
├── app.py # Flask主程序
├── templates/ # 模板目录
│ └── index.html # Jinja2模板
└── static/ # 静态资源(CSS/JS/图片)
2. 基本渲染示例
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html',
title='首页',
user={'name': '张三', 'age': 25})
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
</head>
<body>
<h1>欢迎, {{ user.name }}!</h1>
{% if user.age >= 18 %}
<p>您是成年人</p>
{% else %}
<p>您是未成年人</p>
{% endif %}
</body>
</html>
二、高级模板技巧
1. 模板继承(Layout系统)
<!-- templates/layout.html -->
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}</title>
{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
<!-- templates/page.html -->
{% extends "layout.html" %}
{% block title %}子页面{% endblock %}
{% block content %}
<h1>这是子页面内容</h1>
{% endblock %}
2. 宏(Macros)实现组件复用
<!-- templates/macros.html -->
{% macro render_user(user) %}
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>年龄: {{ user.age }}</p>
</div>
{% endmacro %}
<!-- 使用宏 -->
{% from "macros.html" import render_user %}
{{ render_user({'name': '李四', 'age': 30}) }}
三、动态数据与JS交互
1. 直接传递JSON到JS
# Flask路由
@app.route('/data')
def get_data():
return render_template('data.html',
items=[1, 2, 3],
config={'debug': True})
<script>
const APP_CONFIG = {{ config | tojson | safe }};
const ITEMS = {{ items | tojson | safe }};
console.log(APP_CONFIG.debug); // true
ITEMS.forEach(item => console.log(item));
</script>
2. AJAX动态加载(推荐)
# 提供JSON API
@app.route('/api/data')
def api_data():
return jsonify({'data': [4,5,6]})
// 前端通过fetch获取
fetch('/api/data')
.then(res => res.json())
.then(data => {
document.getElementById('output').innerHTML =
`服务器数据: ${data.data.join(', ')}`;
});
四、常见问题解决方案
1. 缓存问题
开发时禁用缓存:
@app.after_request
def add_header(response):
if 'Cache-Control' not in response.headers:
response.headers['Cache-Control'] = 'no-store'
return response
2. 处理表单数据
@app.route('/submit', methods=['POST'])
def submit():
username = request.form.get('username')
return render_template('result.html', username=username)
<form method="POST" action="/submit">
<input type="text" name="username">
<button type="submit">提交</button>
</form>
五、性能优化建议
模板缓存(生产环境启用):
app.config['TEMPLATES_AUTO_RELOAD'] = False # 生产环境设为False
静态文件版本控制:
<link href="/static/css/style.css?v={{ config.VERSION }}" rel="stylesheet">
异步加载:
<script defer src="{{ url_for('static', filename='js/app.js') }}"></script>
六、安全注意事项
始终转义变量:
<!-- 安全 --> <p>{{ user_input | escape }}</p> <!-- 危险!避免直接渲染HTML --> <p>{{ user_input | safe }}</p>
内容安全策略(CSP):
@app.after_request def add_csp(response): response.headers['Content-Security-Policy'] = "default-src 'self'" return response
七、完整工作流程示例
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/search')
def search():
query = request.args.get('q', '')
results = [] # 这里替换为实际搜索逻辑
return render_template('search.html',
query=query,
results=results)
if __name__ == '__main__':
app.run(debug=True)
<!-- templates/search.html -->
{% extends "layout.html" %}
{% block content %}
<form action="/search">
<input type="text" name="q" value="{{ query }}">
<button>搜索</button>
</form>
<ul>
{% for item in results %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock %}
通过以上方法,您可以高效地在Flask中实现:
- 动态HTML渲染
- 前后端数据交互
- 组件化开发
- 安全的内容输出
关键点是合理使用Jinja2的模板继承、控制结构和过滤器,同时注意安全性和性能优化。