现在位置: 首页 > Flask 教程 > 正文

Jinja2 模板 — 渲染 HTML 页面

本章你将学会用 Jinja2 模板引擎渲染完整的 HTML 页面,抽取公共布局。


从字符串到模板

上一章视图函数直接返回字符串,里行嵌 HTML 标签,无法复用、难以维护。

render_template() 是 Flask 渲染模板的入口:它加载 HTML 文件、注入变量、返回完整的 HTML 响应。

实例

# 文件路径:app.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    # render_template('模板文件名', 变量名=值, ...)
    return render_template('index.html', title='RUNOOB 博客')

Flask 默认在项目根目录的 templates/ 文件夹中查找模板。


创建模板文件

在项目根目录下创建 templates/ 文件夹,然后创建 index.html

实例

<!-- 文件路径:templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ title }}</h1>
    <p>欢迎光临,这是用 Jinja2 模板引擎渲染的页面。</p>
</body>
</html>

Jinja2 模板语法速览

Jinja2 语法与 Django 模板语法几乎完全一样,也与 Vue3 的插值语法类似。

用途Jinja2 / Django说明
输出变量{{ variable }}支持属性访问:{{ user.name }}
循环{% for item in list %}...{% endfor %}支持 loop.index
条件{% if cond %}...{% endif %}可链式:{% elif %}
继承{% extends 'base.html' %}必须在第一行
块定义{% block name %}...{% endblock %}子模板填充父模板的空位

Jinja2 和 Django 模板的语法几乎一致,但 Jinja2 更灵活:你可以在模板中写更复杂的 Python 表达式,Django 模板则故意限制了一些能力以避免业务逻辑泄漏到模板中。


模板继承:base.html

多个页面共享导航栏、页脚,用 模板继承 避免重复 HTML。

创建 base.html 父模板

实例

<!-- 文件路径:templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- block title:子模板可以覆盖页面标题 -->
    <title>{% block title %}RUNOOB 博客{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f5f5f5;
            color: #333;
        }
        .navbar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 16px 40px; background: #fff;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .navbar .logo { font-size: 22px; font-weight: bold; color: #e74c3c; text-decoration: none; }
        .navbar a { margin-left: 20px; text-decoration: none; color: #333; }
        .container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
        .footer { text-align: center; padding: 20px; color: #999; border-top: 1px solid #eee; }
    </style>
    {% block extra_head %}{% endblock %}
</head>
<body>
    <header class="navbar">
        <a href="/" class="logo">RUNOOB 博客(Flask)</a>
        <nav>
            <a href="/">首页</a>
            <a href="/about">关于</a>
        </nav>
    </header>

    <main class="container">
        <!-- block content:子模板在此处填入页面主体内容 -->
        {% block content %}
        {% endblock %}
    </main>

    <footer class="footer">
        <p>© 2024 RUNOOB 博客. Powered by Flask.</p>
    </footer>
</body>
</html>

子模板继承父模板

实例

<!-- 文件路径:templates/index.html -->
{% extends 'base.html' %}

{% block title %}RUNOOB 博客 - 首页{% endblock %}

{% block content %}
<h2>最新文章</h2>
<p>文章列表将在后续章节展示。</p>
{% endblock %}

{% extends 'base.html' %} 必须写在子模板的第一行(注释除外)。这是 Jinja2 的硬性要求——模板引擎需要最先确定继承关系。


url_for() 生成链接

不要硬编码 URL,用 url_for() 通过函数名反查地址。

实例

# views.py 中定义路由端点(endpoint)
@app.route("/")
def index(): ...

@app.route("/post/<int:post_id>")
def post_detail(post_id): ...

实例

[mycode4 type="html"> 首页 文章 3 [/mycode4]

路由变了(如 /post/ 改成 /article/),模板中的 url_for() 自动生成新地址。


静态文件配置

Flask 默认将 static/ 目录作为静态文件根目录。

blog_project/
├── static/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── main.js
│   └── images/
│       └── logo.png
├── templates/
└── app.py

模板中通过 url_for('static', filename='css/style.css') 引用。


本章小结

本章你掌握了 Jinja2 模板的核心:render_template() 渲染模板、{{ }} 输出变量、{% for/if %} 控制流、base.html + block + extends 模板继承、url_for() 生成链接。

现在博客页面变成了结构清晰的 HTML,公共部分抽取到了 base.html 中。