HTML 模块化方案

在 FastAPI 中拆分大型 HTML,推荐使用 Jinja2 模板引擎,它既支持模板继承又支持独立运行。

方案一:模板继承(推荐)

  1. 目录结构

project/

├── main.py

├── templates/

│ ├── base.html # 基础模板

│ ├── components/

│ │ ├── header.html # 可独立运行

│ │ ├── sidebar.html # 可独立运行

│ │ └── footer.html # 可独立运行

│ └── pages/

│ └── index.html # 主页面

└── static/

├── css/

└── js/

  1. 基础模板(base.html)
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}默认标题{% endblock %}</title>
    <link rel="stylesheet" href="/static/css/main.css">
    {% block extra_css %}{% endblock %}
</head>

<body>

    {% block content %}{% endblock %}

    <script src="/static/js/main.js"></script>

    {% block extra_js %}{% endblock %}

</body>

</html>
  1. 独立可运行的组件(header.html)
html 复制代码
{% extends "base.html" %}

{% block title %}Header Component{% endblock %}

{% block content %}

<header class="header">
    <nav>

        <a href="/">首页</a>

        <a href="/about">关于</a>

        <a href="/contact">联系</a>

    </nav>
</header>

<!-- 独立运行时的测试内容 -->
{% if standalone %}

<div style="padding: 20px; background: #f0f0f0;">

    <h3>这是 Header 组件的独立预览</h3>

</div>

{% endif %}

{% endblock %}
  1. 主页面组合(index.html)
html 复制代码
{% extends "base.html" %}
{% block title %}首页{% endblock %}

{% block content %}

    {% include "components/header.html" %}

    <main class="main-content">

        <h1>主要内容区域</h1>

        {% include "components/sidebar.html" %}
 
        <div class="content">

            <!-- 具体内容 -->

        </div>

    </main>
 
    {% include "components/footer.html" %}

{% endblock %}
  1. FastAPI 后端代码(main.py
python 复制代码
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse


app = FastAPI()

# 挂载静态文件
app.mount("/static", StaticFiles(directory="static"), name="static")

# 配置模板
templates = Jinja2Templates(directory="templates")

# 主页面路由
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):

    return templates.TemplateResponse(

        "pages/index.html",

        {"request": request}
    )


# 组件独立预览路由
@app.get("/preview/header", response_class=HTMLResponse)
async def preview_header(request: Request):

    return templates.TemplateResponse(

        "components/header.html",

        {"request": request, "standalone": True}
    )


@app.get("/preview/sidebar", response_class=HTMLResponse)
async def preview_sidebar(request: Request):
    return templates.TemplateResponse(

        "components/sidebar.html",

        {"request": request, "standalone": True}
    )

方案二:使用宏(Macros)

组件定义(macros.html)

html 复制代码
{% macro render_card(title, content, image_url) %}

<div class="card">

    {% if image_url %}

        <img src="{{ image_url }}" alt="{{ title }}">

    {% endif %}

    <h3>{{ title }}</h3>

    <p>{{ content }}</p>

</div>

{% endmacro %}



{% macro render_button(text, type="primary", url="#") %}

<a href="{{ url }}" class="btn btn-{{ type }}">{{ text }}</a>

{% endmacro %}

使用宏

html 复制代码
{% from "components/macros.html" import render_card, render_button %}

{% extends "base.html" %}

{% block content %}

{{ render_card("标题", "内容描述", "/static/img/photo.jpg") }}

{{ render_button("点击我", "success", "/action") }}

{% endblock %}

方案三:动态加载(适合大型应用)

使用 HTMX 实现动态加载

html 复制代码
<!-- 主页面 -->

<div id="header-container"

     hx-get="/api/components/header"

     hx-trigger="load"

     hx-swap="innerHTML">

    加载中...

</div>

FastAPI 路由

python 复制代码
@app.get("/api/components/header")
async def get_header_component(request: Request):

    return templates.TemplateResponse(

        "components/header.html",

        {"request": request},

        # 只返回片段,不包含完整HTML结构
    )

最佳实践建议

  1. 组件设计原则
python 复制代码
# 为组件添加上下文管理
@app.get("/components/{component_name}")
async def render_component(

    component_name: str,

    request: Request,

    standalone: bool = False
):

    """
    standalone=True: 返回完整HTML(用于独立预览)
    standalone=False: 返回片段(用于include)
    """

    context = {

        "request": request,

        "standalone": standalone,

        # 添加组件所需的数据
    }

    return templates.TemplateResponse(
        f"components/{component_name}.html",
        context
    )
  1. 性能优化
python 复制代码
from functools import lru_cache

@lru_cache(maxsize=128)
def get_template_content(template_name: str):

    """缓存静态模板内容"""

    return templates.get_template(template_name)
  1. 组件通信
html 复制代码
<!-- 使用自定义事件 -->
<script>

document.addEventListener('headerLoaded', function(e) {

    console.log('Header组件已加载', e.detail);

});

</script>

完整示例

sidebar.html(独立可运行)

html 复制代码
{% if not embedded %}

<!DOCTYPE html>
<html>
<head>

    <title>Sidebar Preview</title>

    <link rel="stylesheet" href="/static/css/main.css">

</head>
<body>

{% endif %}

<aside class="sidebar">
    <ul>

        <li><a href="/dashboard">仪表板</a></li>

        <li><a href="/settings">设置</a></li>

    </ul>
</aside>


{% if not embedded %}

</body>
</html>

{% endif %}

使用时传递 embedded 参数

python 复制代码
# 作为组件使用
templates.TemplateResponse("components/sidebar.html", {

    "request": request,

    "embedded": True
})



# 独立预览
templates.TemplateResponse("components/sidebar.html", {

    "request": request,

    "embedded": False
})

这种方案既保证了运行流畅(通过模板继承和缓存),又能让组件独立运行(通过条件判断)。

相关推荐
灯把黑夜烧了一个洞2 小时前
2026年跨年倒计时网页版
javascript·css·html·2026跨年代码·新年代码
小满zs2 小时前
Next.js第十九章(服务器函数)
前端·next.js
仰望.2 小时前
vxe-table 如何实现分页勾选复选框功能,分页后还能支持多选的选中状态
前端·vue.js·vxe-table
铅笔侠_小龙虾2 小时前
html+css 实现键盘
前端·css·html
licongmingli2 小时前
vue2 基于虚拟dom的下拉选择框,保证大数据不卡顿,仿antd功能和样式
大数据·前端·javascript·vue.js·anti-design-vue
小笔学长2 小时前
Webpack 入门:打包工具的基本使用
前端·webpack·前端开发·入门教程·前端打包优化
黎明初时2 小时前
react基础框架搭建4-tailwindcss配置:react+router+redux+axios+Tailwind+webpack
前端·react.js·webpack·前端框架
小沐°2 小时前
vue3-父子组件通信
前端·javascript·vue.js
铅笔侠_小龙虾2 小时前
Ubuntu 搭建前端环境&Vue实战
linux·前端·ubuntu·vue