使用 HTMX 實現 Django 頁面不刷新交互效果
更新日期: 2024 年 12 月 1 日
本文為 Django 與前端框架教學,第 2 篇:
- 使用 Esbuild 實現 Django 中的靜態資源管理與優化
- 使用 HTMX 實現 Django 頁面不刷新交互效果 👈 所在位置
- 使用 HTMX 完成刪除功能的邏輯解析
- 使用 Tailwind CSS 和 Shared Template 設計 Django 項目導航欄與樣式
建議閱讀本文前,先閱讀完 Django 高階教學 系列文
在傳統 Django 開發中,每次操作如表單提交或數據更新,往往需要整頁刷新。
通過結合 HTMX,我們可以實現無刷新數據提交與更新的效果,大幅提升用戶體驗。
為什麼選擇 HTMX?
傳統方法的挑戰
- DOM 操作的複雜性:傳統的 JavaScript 需要手動操作 DOM 樹,代碼不易管理。
- 全頁刷新:每次提交數據後需重新加載整個頁面,影響用戶體驗。
- 代碼冗長:即便使用 Fetch API 或框架,代碼結構仍可能變得混亂。
HTMX 的優勢
- 輕量且簡單:只需引入一個腳本文件即可啟用。
- HTML 驅動:通過直接操作 HTML 標籤屬性,實現 AJAX 請求和 DOM 更新。
- 支持片段渲染:後端直接返回 HTML 模板片段,無需編寫複雜的 JSON 解析邏輯。
配置環境與初始化
使用 CDN 安裝 HTMX
在模板文件中加載 HTMX:
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
使用 NPM 安裝 HTMX(可選)
如果需要使用模組化開發,可使用 NPM 安裝:
npm install htmx.org
然後在前端打包工具(如 Esbuild)的代碼中引入:
檔案位置:frontend/scripts/app.js
在這個檔案中,我們將導入需要的 HTMX 模組。
import "htmx.org";
調整 HTML 表單
檔案位置:resumes/templates/resumes/show.html
我們調整留言表單,使用 HTMX 的屬性進行動態更新。
原始代碼:
<section>
<form action="{% url 'resumes:comments' resume.id %}" method="post">
{% csrf_token %}
<textarea name="content" id=""></textarea>
<button>新增留言</button>
</form>
</section>
<ul>
{% for comment in comments %}
<li>
{{ comment.content|linebreaksbr }}
<form action="{% url 'comments:delete' comment.id %}" method="post">
{% csrf_token %}
<button>刪</button>
</form>
{{ comment.created_at }}
</li>
{% endfor %}
</ul>
調整代碼:
使用 HTMX 的 hx-swap
屬性靈活控制內容插入方式。
<form
hx-post="{% url 'resumes:comments' resume.id %}"
hx-target="#comment-list"
hx-swap="afterbegin"
>
{% csrf_token %}
<textarea name="content" id=""></textarea>
<button>新增留言</button>
</form>
<ul id="comment-list">
<!-- 最新留言會被插入到這裡的頂部 -->
</ul>
標籤詳解
hx-post
- 功能:設置向後端提交數據的 URL,實現表單的非同步提交。
- 應用場景:例如,當用戶提交留言時,
hx-post
可以將表單的數據直接傳送到後端的 API 端點,無需刷新頁面。 - 代碼範例:
<form hx-post="/comments/submit/" hx-target="#comment-list">
<textarea name="content"></textarea>
<button type="submit">新增留言</button>
</form>
hx-target
- 功能:指定後端返回的數據,要更新的 DOM 元素。
- 應用場景:比如留言提交成功後,將新留言內容插入到特定的留言列表中。
- 代碼範例:
<form hx-post="/comments/submit/" hx-target="#comment-list">
...
</form>
hx-swap
- 功能:控制如何將後端返回的數據,插入到目標元素中。
- 可選值:
innerHTML
(默認值):替換目標元素的內部 HTML。beforeend
:將新內容追加到目標元素的結尾處。afterbegin
:將新內容插入到目標元素的開頭處。
- 代碼範例:
<form hx-post="/comments/submit/" hx-target="#comment-list">
...
</form>
效果說明
hx-swap="afterbegin"
的作用- 功能:當後端返回的數據到達時,將新內容插入到目標元素的頂部。
- 應用場景:適用於需要按時間排序的留言列表,讓最新留言顯示在列表的第一個位置。
- 用戶體驗提升
- 當用戶提交新留言後,無需刷新頁面,即可即時看到新內容出現在列表頂部。
- 簡化了前後端的交互流程,避免了傳統方式中重新渲染整個頁面的開銷。
模板進階重構:使用變數傳遞數據
新增 HTML 模板片段
檔案位置:comments/templates/comments/_comment.html
建立一個只渲染單個留言的模板,讓 HTMX 插入的內容更高效。
代碼範例:
<li>
{{ comment.content|linebreaksbr }} <!-- 顯示留言內容,支持換行 -->
<form action="{% url 'comments:delete' comment.id %}" method="post">
{% csrf_token %} <!-- 防止 CSRF 攻擊 -->
<button>刪</button> <!-- 刪除按鈕 -->
</form>
{{ comment.created_at }} <!-- 顯示留言的創建時間 -->
</li>
重點解析
- 模板片段的作用
- 單個留言渲染:只渲染新增的留言部分,避免重新渲染整個列表,提升性能。
- 高效更新:模板片段直接返回,前端只需插入到指定位置(例如列表頂部)。
- 返回方式的優勢
- 減少數據傳輸:返回的 HTML 僅包含新增的留言部分,而非整個頁面。
- 高效頁面渲染:前端只需插入新增的 HTML,而不需重新載入整個頁面。
- 即時交互:配合 HTMX 的屬性(如
hx-swap
),實現無刷新動態更新。
修改模板
檔案位置:resumes/templates/resumes/show.html
使用 Django 的 include
標籤,重構模板邏輯,將數據與模板片段分離,便於動態更新。
<ul id="comment-list">
{% for comment in comments %}
{% include "comments/_comment.html" with comment=comment %}
{% endfor %}
</ul>
修改視圖函數
檔案位置:comments/views.py
在後端的視圖中,我們使用 Django 的模板引擎,返回 HTML 片段而非整頁重定向。
具體步驟:
- 匯入必要模組
get_object_or_404
:快速獲取目標物件,若不存在直接返回 404 錯誤。render
:用於渲染模板並返回結果。HttpResponse
:可用於返回非模板內容(例如純文本或 JSON)。- 模型
Comment
和關聯的Resume
。
- 新增視圖函數
add_comment
此函數處理新增留言的提交邏輯,並返回渲染後的 HTML 片段。
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse
from .models import Comment
from resumes.models import Resume
def add_comment(request, id):
if request.method == "POST": # 確保只處理 POST 請求
# 根據 id 獲取對應的 Resume,如果不存在返回 404
resume = get_object_or_404(Resume, id=id)
# 獲取用戶提交的留言內容
content = request.POST.get("content")
# 創建並保存新的 Comment 物件
comment = resume.comment_set.create(content=content)
# 返回渲染後的 HTML 模板片段
return render(request, "comments/_comment.html", {"comment": comment})
執行邏輯:
- 確保請求為
POST
方法,避免誤操作。 - 根據
Resume
的id
找到對應的簡歷物件。 - 從請求的
POST
數據中提取content
,作為新留言的內容。 - 通過
comment_set.create
方法創建並保存留言,保證數據關聯性。 - 使用
render
返回渲染好的 HTML 模板片段,供前端直接插入頁面。
模板中 with
語法的重點解析
with comment=comment
的作用- 功能:將父模板中的變數傳遞給子模板,便於在子模板中使用局部變數進行渲染。
- 語法解釋:
- 父模板中的(等號後面)
comment
:來自數據庫查詢結果,通常在視圖函數中傳遞給模板。 - 子模板中的(等號前面)
comment
:用於渲染單個留言的局部變數,通過with
語法傳入。
- 父模板中的(等號後面)
- 代碼範例:父模板中的代碼:
<ul>
{% for comment in comments %}
{% include "comments/_comment.html" with comment=comment %}
{% endfor %}
</ul>
- 子模板
comments/_comment.html
中的代碼:
<li>
{{ comment.content|linebreaksbr }}
<form hx-target="closest li" hx-swap="outerHTML" hx-post="{% url 'comments:delete' comment.id %}">
{% csrf_token %}
<button>刪</button>
</form>
{{ comment.created_at }}
</li>
- 可讀性與重用性提升
- 通過將留言項目提取到子模板
_comment.html
,可以更高效地進行模板的重用和維護。 - 使用
with
語法進行變數傳遞,避免直接操作全局變數,提高了代碼的清晰度和模塊化程度。
- 通過將留言項目提取到子模板
目錄結構
以下是上述實現動態留言功能相關的目錄結構,方便新手快速對應:
mysite/
├── manage.py # Django 專案入口
├── mysite/ # 專案設定目錄
│ ├── settings.py # 專案配置檔案
│ ├── urls.py # 主路由設置
│ ├── wsgi.py # WSGI 應用入口
│ └── asgi.py # ASGI 應用入口
│
├── resumes/ # 簡歷應用
│ ├── models.py # 定義 Resume 模型
│ ├── views.py # 定義簡歷視圖邏輯
│ ├── urls.py # 簡歷路由設置
│ ├── templates/ # 簡歷相關模板
│ │ └── resumes/
│ │ └── show.html # 顯示簡歷詳細與留言區
│ └── __init__.py # 模塊初始化文件
│
├── comments/ # 留言應用
│ ├── models.py # 定義 Comment 模型
│ ├── views.py # 定義留言處理邏輯
│ ├── urls.py # 留言路由設置
│ ├── templates/ # 留言相關模板
│ │ └── comments/
│ │ └── _comment.html # 單個留言模板片段
│ └── __init__.py # 模塊初始化文件
│
├── static/ # 靜態文件目錄
│ ├── scripts/
│ │ └── app.js # 前端 JS 文件
│ └── styles/
│ └── app.css # 前端樣式文件
│
└── templates/ # 全局模板
└── shared/
└── layout.html # 主模板,包含 JS 和 CSS 引用
結構說明
mysite/
:專案根目錄,包含 Django 的主要設定檔案。resumes/
:簡歷相關功能,包含簡歷模型、視圖、路由及模板,show.html
中呈現留言區和新增留言的表單。comments/
:留言相關功能,處理留言的模型、視圖、模板,以及單個留言的模板片段_comment.html
。static/
:靜態文件目錄,存放前端的 JS 和 CSS 文件,供模板引用。templates/shared/
:全局模板目錄,存放公共布局文件如layout.html
。
這樣的結構使專案清晰、模塊化,方便擴展與維護。
小結
通過結合 Django 和 HTMX,我們實現了無刷新留言功能,並解決了傳統頁面刷新帶來的用戶體驗問題。其核心步驟包括:
- HTML 驅動的交互:簡化 JavaScript 邏輯。
- 模板片段渲染:後端直接返回局部 HTML,提高效率。
- 進階功能擴展:靈活使用 HTMX 的功能(如
swap
)進一步提升頁面效果。
這種設計模式特別適合希望快速構建高效頁面的 Django 開發者!