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
})

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

相关推荐
ℋᙚᵐⁱᒻᵉ鲸落4 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙5 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
前端snow6 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上6 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈7 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香7 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest7 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士8 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见8 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen9 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试