目錄
htmx 是一個允許您直接從 HTML 存取現代瀏覽器功能的程式庫,而無需使用 javascript。
要了解 htmx,首先讓我們看看一個錨點標籤
<a href="/blog">Blog</a>
這個錨點標籤告訴瀏覽器
「當使用者點擊這個連結時,向 ‘/blog’ 發出 HTTP GET 請求,並將回應內容載入到瀏覽器視窗中」。
記住這一點,請考慮以下 HTML 片段
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML"
>
Click Me!
</button>
這告訴 htmx
「當使用者點擊這個按鈕時,向 ‘/clicked’ 發出 HTTP POST 請求,並使用回應中的內容來取代 DOM 中 id 為
parent-div
的元素」
htmx 擴展並概括了 HTML 作為超文字的核心概念,直接在語言中開啟了更多可能性
GET
和 POST
請注意,當您使用 htmx 時,伺服器端通常會回應 HTML,而不是 JSON。這使您牢牢地保持在 原始的網頁程式設計模型中,使用 超文字作為應用程式狀態引擎,甚至不需要真正理解這個概念。
值得一提的是,如果您願意,您可以在使用 htmx 時使用 data-
字首
<a data-hx-post="/click">Click Me!</a>
最後,htmx 的第 1 版仍然受支援,並支援 IE11。
如果您要從 htmx 1.x 遷移到 htmx 2.x,請參閱 htmx 1.x 遷移指南。
如果您要從 intercooler.js 遷移到 htmx,請參閱 intercooler 遷移指南。
Htmx 是一個不需依賴項、面向瀏覽器的 javascript 程式庫。這表示使用它就像將 <script>
標籤添加到您的文件標頭一樣簡單。不需要建置系統來使用它。
開始使用 htmx 的最快方法是透過 CDN 載入它。您可以簡單地將此添加到您的標頭標籤並開始使用
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
也有提供未縮小的版本
<script src="https://unpkg.com/htmx.org@2.0.3/dist/htmx.js" integrity="sha384-BBDmZzVt6vjz5YbQqZPtFZW82o8QotoM7RUp5xOxV3nSJ8u2pSdtzFAbGKzTlKtg" crossorigin="anonymous"></script>
雖然 CDN 方法非常簡單,但您可能需要考慮 不要在生產環境中使用 CDN。
安裝 htmx 的第二種最簡單方法是將其複製到您的專案中。
從 unpkg.com 下載 htmx.min.js
,並將其添加到您專案中適當的目錄中,並在需要的地方使用 <script>
標籤包含它
<script src="/path/to/htmx.min.js"></script>
對於 npm 樣式的建置系統,您可以透過 npm 安裝 htmx
npm install htmx.org@2.0.3
安裝後,您需要使用適當的工具來使用 node_modules/htmx.org/dist/htmx.js
(或 .min.js
)。例如,您可能會將 htmx 與一些擴充功能和專案特定的程式碼一起打包。
如果您使用 webpack 來管理您的 javascript
htmx
index.js
中import 'htmx.org';
如果您想要使用全域 htmx
變數 (建議使用),您需要將其注入到視窗範圍中
index.js
中 (在步驟 2 中的 import 之下)import 'path/to/my_custom.js';
window.htmx = require('htmx.org');
htmx 的核心是一組屬性,允許您直接從 HTML 發出 AJAX 請求
屬性 | 說明 |
---|---|
hx-get | 向給定的 URL 發出 GET 請求 |
hx-post | 向給定的 URL 發出 POST 請求 |
hx-put | 向給定的 URL 發出 PUT 請求 |
hx-patch | 向給定的 URL 發出 PATCH 請求 |
hx-delete | 向給定的 URL 發出 DELETE 請求 |
這些屬性中的每一個都接受一個 URL 來發出 AJAX 請求。當元素被觸發時,元素將向給定的 URL 發出指定類型的請求
<button hx-put="/messages">
Put To Messages
</button>
這告訴瀏覽器
當使用者點擊這個按鈕時,向 URL /messages 發出 PUT 請求,並將回應載入到按鈕中
預設情況下,AJAX 請求是由元素的「自然」事件觸發的
input
、textarea
和 select
在 change
事件上觸發form
在 submit
事件上觸發click
事件觸發如果您想要不同的行為,可以使用 hx-trigger 屬性來指定哪個事件將導致請求。
這裡有一個 div
,當滑鼠進入時,會向 /mouse_entered
發出 POST 請求
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
觸發器也可以有一些額外的修飾符來改變其行為。例如,如果您希望請求只發生一次,您可以使用觸發器的 once
修飾符
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Here Mouse, Mouse!]
</div>
您可以對觸發器使用其他修飾符
changed
- 僅在元素的值已更改時發出請求delay:<時間間隔>
- 等待給定的時間量 (例如 1s
) 後再發出請求。如果事件再次觸發,則會重置倒數計時。throttle:<時間間隔>
- 等待給定的時間量 (例如 1s
) 後再發出請求。與 delay
不同,如果在達到時間限制之前發生新事件,則該事件將被捨棄,因此請求將在時間段結束時觸發。from:<CSS 選擇器>
- 在不同的元素上監聽事件。這可以用於鍵盤快捷鍵之類的操作。請注意,如果頁面變更,則不會重新評估此 CSS 選擇器。您可以使用這些屬性來實作許多常見的 UX 模式,例如 動態搜尋
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
如果輸入已變更,則此輸入將在按鍵抬起事件後的 500 毫秒發出請求,並將結果插入 id 為 search-results
的 div
中。
多個觸發器可以在 hx-trigger 屬性中指定,以逗號分隔。
您也可以在事件名稱後面使用方括號來應用觸發器過濾器,括號中包含將被評估的 javascript 運算式。如果運算式評估為 true
,則事件將觸發,否則不會。
這是一個僅在元素的 Control-Click 上觸發的範例
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
諸如 ctrlKey
之類的屬性將首先針對觸發事件解析,然後針對全域範圍解析。 this
符號將設定為目前的元素。
htmx 提供了一些特殊事件,用於 hx-trigger 中
load
- 當元素首次載入時觸發一次revealed
- 當元素首次捲動到視口中時觸發一次intersect
- 當元素首次與視口相交時觸發一次。這支援兩個額外的選項root:<選擇器>
- 交叉點的根元素的 CSS 選擇器threshold:<浮點數>
- 介於 0.0 和 1.0 之間的浮點數,表示要觸發事件的交叉量如果您有進階的使用案例,也可以使用自訂事件來觸發請求。
如果您希望元素輪詢給定的 URL,而不是等待事件,您可以使用 hx-trigger
屬性的 every
語法
<div hx-get="/news" hx-trigger="every 2s"></div>
這告訴 htmx
每 2 秒,向 /news 發出 GET 請求,並將回應載入到 div 中
如果您想從伺服器回應停止輪詢,您可以使用 HTTP 回應碼 286
回應,並且該元素將取消輪詢。
在 htmx 中可以用來實現輪詢的另一種技術是「載入輪詢」,其中元素指定一個帶有延遲的 load
觸發器,並將自身替換為回應
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML"
>
</div>
如果 /messages
端點持續返回以這種方式設定的 div,它將每秒持續「輪詢」回 URL。
在需要輪詢具有終點的情況下,載入輪詢會很有用,例如當您向使用者顯示進度條時。
當發出 AJAX 請求時,最好讓使用者知道正在發生某些事情,因為瀏覽器不會給予他們任何回饋。您可以使用 htmx-indicator
類別在 htmx 中實現此目的。
htmx-indicator
類別的定義是,任何具有此類別的元素的透明度預設為 0,使其在 DOM 中不可見但存在。
當 htmx 發出請求時,它會將 htmx-request
類別放在元素上(可以是請求元素,也可以是另一個指定的元素)。htmx-request
類別會導致具有 htmx-indicator
類別的子元素轉換為透明度 1,顯示指示器。
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
這裡我們有一個按鈕。當點擊它時,htmx-request
類別將被新增到它,這將顯示旋轉圖示元素。(我現在喜歡SVG 旋轉圖示。)
雖然 htmx-indicator
類別使用透明度來隱藏和顯示進度指示器,但如果您更喜歡其他機制,您可以建立自己的 CSS 轉換,如下所示
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}
如果您想將 htmx-request
類別新增到不同的元素,您可以使用具有 CSS 選擇器的 hx-indicator 屬性來實現
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
在這裡,我們通過 id 明確地呼叫指示器。請注意,我們也可以將類別放在父 div
上,並具有相同的效果。
您也可以使用 hx-disabled-elt 屬性,在請求期間將 disabled
屬性新增到元素。
如果您希望將回應載入到發出請求的元素之外的其他元素中,您可以使用採用 CSS 選擇器的 hx-target 屬性。回顧我們的「即時搜尋」範例
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
您可以看到搜尋結果將載入到 div#search-results
中,而不是輸入標籤中。
hx-target
和大多數採用 CSS 選擇器的屬性都支援「擴展的」CSS 語法
this
關鍵字,它表示 hx-target
屬性所在的元素是目標closest <CSS 選擇器>
語法將找到符合給定 CSS 選擇器的最近的祖先元素或自身。(例如,closest tr
將針對最接近元素的表格列)next <CSS 選擇器>
語法將在 DOM 中找到符合給定 CSS 選擇器的下一個元素。previous <CSS 選擇器>
語法將在 DOM 中找到符合給定 CSS 選擇器的上一個元素。find <CSS 選擇器>
將找到符合給定 CSS 選擇器的第一個子後代元素。(例如,find tr
將針對元素的第一個子後代列)此外,CSS 選擇器可以用 <
和 />
字元包裝,模仿 hyperscript 的 查詢文字語法。
像這樣的相對目標對於建立靈活的使用者介面很有用,而無需在您的 DOM 中添加大量 id
屬性。
htmx 提供幾種不同的方式將返回的 HTML 替換到 DOM 中。預設情況下,內容會取代目標元素的 innerHTML
。您可以使用 hx-swap 屬性以及以下任何值來修改此行為
名稱 | 說明 |
---|---|
innerHTML | 預設值,將內容放在目標元素內 |
outerHTML | 將整個目標元素替換為返回的內容 |
afterbegin | 將內容放在目標內第一個子元素之前 |
beforebegin | 將內容放在目標的父元素中目標之前 |
beforeend | 將內容放在目標內最後一個子元素之後 |
afterend | 將內容放在目標的父元素中目標之後 |
delete | 刪除目標元素,無論回應為何 |
none | 不附加來自回應的內容(帶外替換和回應標頭仍會被處理) |
除了上述標準的替換機制之外,htmx 還透過擴充功能支援形態替換。形態替換嘗試將新內容合併到現有的 DOM 中,而不是簡單地取代它。它們通常在交換操作期間透過就地變異現有節點,在保留焦點、影片狀態等內容方面做得更好,但代價是更多的 CPU。
以下擴充功能可用於形態樣式的替換
新的實驗性 檢視轉換 API 為開發人員提供了一種在不同 DOM 狀態之間建立動畫轉換的方法。它仍在積極開發中,並非所有瀏覽器都可用,但 htmx 提供了一種與此新 API 協作的方式,如果給定瀏覽器中沒有可用的 API,則會回復到非轉換機制。
您可以使用以下方法來試用這個新 API
htmx.config.globalViewTransitions
配置變數設定為 true
,以對所有替換使用轉換hx-swap
屬性中使用 transition:true
選項htmx:beforeTransition
事件並在其上呼叫 preventDefault()
以取消轉換。可以使用 CSS 設定檢視轉換,如該功能的 Chrome 文件中所述。
您可以在動畫範例頁面上查看檢視轉換範例。
hx-swap 屬性支援許多選項,用於調整 htmx 的替換行為。例如,預設情況下,htmx 會替換新內容中找到的任何標題標籤的標題。您可以將 ignoreTitle
修飾符設定為 true 來關閉此行為
<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>
hx-swap
上可用的修飾符為
選項 | 說明 |
---|---|
transition | true 或 false ,是否針對此替換使用檢視轉換 API |
swap | 要使用的替換延遲(例如,100ms ),在清除舊內容和插入新內容之間 |
settle | 要使用的穩定延遲(例如,100ms ),在新內容插入和穩定之間 |
ignoreTitle | 如果設定為 true ,則新內容中找到的任何標題都將被忽略,並且不會更新文件標題 |
scroll | top 或 bottom ,將目標元素捲動到其頂部或底部 |
show | top 或 bottom ,將目標元素的頂部或底部捲動到視圖中 |
所有替換修飾符都出現在指定的替換樣式之後,並以冒號分隔。
有關這些選項的更多詳細資訊,請參閱 hx-swap 文件。
通常,您希望協調兩個元素之間的請求。例如,您可能希望一個元素的請求取代另一個元素的請求,或者等到另一個元素的請求完成。
htmx 提供 hx-sync
屬性來協助您實現此目的。
請考慮此 HTML 中表單提交和個別輸入驗證請求之間的競爭條件
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Submit</button>
</form>
如果不使用 hx-sync
,填寫輸入並立即提交表單會觸發兩個平行請求到 /validate
和 /store
。
在輸入上使用 hx-sync="closest form:abort"
將監視表單上的請求,並且如果存在表單請求或在輸入請求正在執行時啟動表單請求,則中止輸入的請求
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
這以宣告方式解決了兩個元素之間的同步問題。
htmx 還支援以程式方式取消請求:您可以向元素傳送 htmx:abort
事件,以取消任何正在執行的請求
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
更多範例和詳細資訊可以在 hx-sync
屬性頁面上找到。
htmx 可以輕鬆使用CSS 轉換,而無需 javascript。請考慮此 HTML 內容
<div id="div1">Original Content</div>
假設此內容透過 ajax 請求由 htmx 取代為此新內容
<div id="div1" class="red">New Content</div>
請注意兩件事
red
類別已新增到新內容在這種情況下,我們可以編寫一個從舊狀態到新狀態的 CSS 過渡效果
.red {
color: red;
transition: all ease-in 1s ;
}
當 htmx 替換這個新內容時,它會以 CSS 過渡效果將應用於新內容的方式進行,讓您可以順暢地過渡到新狀態。
總而言之,要對元素使用 CSS 過渡效果,您只需讓其 id
在請求之間保持穩定即可!
您可以查看動畫範例以了解更多詳細資訊和實際演示。
要了解 CSS 過渡效果在 htmx 中實際運作的方式,您必須了解 htmx 使用的底層替換 & 穩定模型。
當從伺服器接收到新內容時,在替換內容之前,會檢查頁面上現有的內容,尋找與 id
屬性相符的元素。如果在新內容中找到相符的元素,則在進行替換之前,會將舊內容的屬性複製到新元素上。然後,會替換新內容,但使用舊的屬性值。最後,在「穩定」延遲(預設為 20 毫秒)後,會替換新的屬性值。這有點瘋狂,但這就是為什麼 CSS 過渡效果可以在沒有開發人員編寫任何 JavaScript 的情況下運作的原因。
如果您想使用 id
屬性將回應中的內容直接替換到 DOM 中,您可以在回應 html 中使用 hx-swap-oob 屬性
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
在此回應中,div#message
將直接替換到相符的 DOM 元素中,而額外的內容將以正常方式替換到目標中。
您可以使用此技術在其他請求上「捎帶」更新。
當表格元素與帶外替換結合使用時可能會出現問題,因為根據 HTML 規範,許多元素不能單獨存在於 DOM 中 (例如 <tr>
或 <td>
)。
為了避免此問題,您可以使用 template
標籤來封裝這些元素
<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>
如果您想選擇回應 HTML 的子集來替換到目標中,您可以使用 hx-select 屬性,該屬性採用 CSS 選擇器,並從回應中選擇相符的元素。
您也可以使用 hx-select-oob 屬性來挑選帶外替換的內容片段,該屬性採用要挑選和替換的元素 ID 清單。
如果有您希望在替換期間保留的內容 (例如,您希望即使發生替換仍能保持播放狀態的影片播放器),您可以在希望保留的元素上使用 hx-preserve 屬性。
預設情況下,導致請求的元素如果有值,則會包含其值。如果元素是表單,它將包含其中所有輸入的值。
與 HTML 表單一樣,輸入的 name
屬性會用作 htmx 發送的請求中的參數名稱。
此外,如果元素導致非 GET
請求,則會包含最近的封閉表單的所有輸入的值。
如果您希望包含其他元素的值,您可以使用 hx-include 屬性,並使用 CSS 選擇器選取您要包含在請求中的所有元素的值。
如果您想過濾掉一些參數,您可以使用 hx-params 屬性。
最後,如果您想以程式方式修改參數,您可以使用 htmx:configRequest 事件。
如果您想透過 htmx 請求上傳檔案,您可以將 hx-encoding 屬性設定為 multipart/form-data
。這將使用 FormData
物件提交請求,該物件將正確地包含請求中的檔案。
請注意,根據您的伺服器端技術,您可能必須以非常不同的方式處理具有此類主體內容的請求。
請注意,htmx 會根據上傳期間的標準 progress
事件定期觸發 htmx:xhr:progress
事件,您可以掛鉤到該事件以顯示上傳進度。
請參閱範例區段,了解更進階的表單模式,包括進度條和錯誤處理。
您可以使用 hx-vals (JSON 格式的名稱-運算式配對) 和 hx-vars 屬性 (動態計算的逗號分隔的名稱-運算式配對) 在請求中包含額外的值。
通常,您會在發出請求之前想要確認動作。htmx 支援 hx-confirm
屬性,它允許您使用簡單的 JavaScript 對話框來確認動作
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
使用事件,您可以實作更複雜的確認對話框。確認範例示範如何使用 sweetalert2 函式庫來確認 htmx 動作。
另一種進行確認的方法是透過 htmx:confirm
事件。此事件在每次觸發請求時都會觸發(不僅在具有 hx-confirm
屬性的元素上觸發),可用於實作請求的非同步確認。
以下是在任何具有 confirm-with-sweet-alert='true'
屬性的元素上使用 sweet alert 的範例
document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Are you sure?",
text: "Are you sure you are sure?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});
htmx 中的大多數屬性都是繼承的:它們會應用於它們所在的元素以及任何子元素。這允許您將屬性「提升」到 DOM 上方,以避免程式碼重複。請考慮以下 htmx
<button hx-delete="/account" hx-confirm="Are you sure?">
Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
Update My Account
</button>
這裡我們有一個重複的 hx-confirm
屬性。我們可以將此屬性提升到父元素
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
此 hx-confirm
屬性現在將應用於其中的所有 htmx 驅動的元素。
有時您希望取消此繼承。假設我們在這個群組中有一個取消按鈕,但不希望它被確認。我們可以像這樣在它上面新增一個 unset
指令
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
<button hx-confirm="unset" hx-get="/">
Cancel
</button>
</div>
那麼,頂部的兩個按鈕會顯示一個確認對話框,但底部的取消按鈕則不會。
可以使用 hx-disinherit
屬性在每個元素和每個屬性的基礎上停用繼承。
如果您希望完全停用屬性繼承,您可以將 htmx.config.disableInheritance
設定變數設定為 true
。這將預設停用繼承,並允許您使用 hx-inherit
屬性明確指定繼承。
Htmx 支援使用 hx-boost 屬性「加強」常規 HTML 錨點和表單。此屬性會將所有錨點標籤和表單轉換為 AJAX 請求,預設情況下,這些請求會以頁面的主體為目標。
以下是一個範例
<div hx-boost="true">
<a href="/blog">Blog</a>
</div>
此 div 中的錨點標籤會向 /blog
發出 AJAX GET
請求,並將回應替換到 body
標籤中。
hx-boost
的一個功能是,如果未啟用 JavaScript,它會優雅地降級:連結和表單會繼續運作,只是它們不會使用 AJAX 請求。這稱為漸進式增強,它允許更廣泛的受眾使用您網站的功能。
其他 htmx 模式也可以調整以實現漸進式增強,但它們需要更多思考。
請考慮主動搜尋範例。按照編寫方式,它不會優雅地降級:未啟用 JavaScript 的人將無法使用此功能。這樣做是為了簡化,以盡可能簡潔地呈現範例。
但是,您可以將 htmx 強化的輸入包裝在表單元素中
<form action="/search" method="POST">
<input class="form-control" type="search"
name="search" placeholder="Begin typing to search users..."
hx-post="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
</form>
這樣做之後,啟用 JavaScript 的用戶端仍然會獲得良好的主動搜尋 UX,但未啟用 JavaScript 的用戶端將能夠按下 Enter 鍵並仍然進行搜尋。更好的是,您也可以新增一個「搜尋」按鈕。然後,您需要使用與 action
屬性對應的 hx-post
來更新表單,或者可能在其上使用 hx-boost
。
您需要在伺服器端檢查 HX-Request
標頭,以區分 htmx 驅動的請求和常規請求,以確定要向用戶端呈現的確切內容。
其他模式可以類似地調整,以實現您應用程式的漸進式增強需求。
如您所見,這需要更多思考和更多工作。它也完全排除了某些功能。這些權衡必須由您(開發人員)根據您專案的目標和受眾來進行。
協助工具是一個與漸進式增強密切相關的概念。使用諸如 hx-boost
之類的漸進式增強技術,將使您的 htmx 應用程式對廣泛的用戶更具協助工具。
基於 htmx 的應用程式與常規的非 AJAX 驅動的 Web 應用程式非常相似,因為 htmx 以 HTML 為導向。
因此,常規的 HTML 協助工具建議適用。例如
透過擴充功能支援 Web Sockets 和伺服器發送事件 (SSE)。請參閱 SSE 擴充功能和 WebSocket 擴充功能頁面以了解更多資訊。
Htmx 提供了一個簡單的機制來與瀏覽器歷史記錄 API互動
如果您希望給定的元素將其請求 URL 推送到瀏覽器導覽列中,並將頁面的目前狀態新增到瀏覽器的歷史記錄中,請包含 hx-push-url 屬性
<a hx-get="/blog" hx-push-url="true">Blog</a>
當使用者按一下此連結時,htmx 將在向 /blog 發出請求之前拍攝目前 DOM 的快照並儲存它。然後,它會執行替換,並將新的位置推送到歷史記錄堆疊上。
當使用者點擊「上一頁」按鈕時,htmx 會從儲存空間中檢索舊內容,並將其交換回目標位置,模擬「回到」先前的狀態。如果快取中找不到該位置,htmx 將向指定的 URL 發出 AJAX 請求,並將標頭 HX-History-Restore-Request
設定為 true,並預期收到整個頁面所需的 HTML。或者,如果將 htmx.config.refreshOnHistoryMiss
設定變數設為 true,則會發出硬式瀏覽器重新整理。
注意: 如果您將 URL 推送到歷史紀錄中,您必須能夠導航到該 URL 並取回完整頁面!使用者可能會將該 URL 複製貼上到電子郵件或新的分頁中。此外,如果頁面不在歷史快取中,htmx 在還原歷史紀錄時也需要整個頁面。
預設情況下,htmx 將使用 body
來擷取和還原歷史快照。這通常是正確的做法,但如果您想使用較小的元素進行快照,您可以使用 hx-history-elt 屬性來指定其他元素。
注意:此元素必須在所有頁面上,否則從歷史紀錄還原時將無法可靠地運作。
如果您正在使用第三方函式庫,並且想要使用 htmx 的歷史紀錄功能,您需要在擷取快照之前清理 DOM。我們以 Tom Select 函式庫為例,它可以讓 select 元素擁有更豐富的使用者體驗。我們將設定 TomSelect 將任何具有 .tomselect
類別的 input 元素轉換為豐富的 select 元素。
首先,我們需要初始化新內容中具有該類別的元素
htmx.onLoad(function (target) {
// find all elements in the new content that should be
// an editor and init w/ TomSelect
var editors = target.querySelectorAll(".tomselect")
.forEach(elt => new TomSelect(elt))
});
這將為所有具有 .tomselect
類別的 input 元素建立豐富的選取器。但是,它會改變 DOM,我們不希望該變更儲存到歷史快取中,因為當歷史內容重新載入到螢幕時,將會重新初始化 TomSelect。
為了處理這個問題,我們需要捕獲 htmx:beforeHistorySave
事件,並透過在其上呼叫 destroy()
來清除 TomSelect 的變更
htmx.on('htmx:beforeHistorySave', function() {
// find all TomSelect elements
document.querySelectorAll('.tomSelect')
.forEach(elt => elt.tomselect.destroy()) // and call destroy() on them
})
這會將 DOM 還原為原始 HTML,從而允許進行乾淨的快照。
可以透過在目前文件中任何元素或 htmx 載入到目前文件中的任何 HTML 片段上,將 hx-history 屬性設定為 false
,來為 URL 停用歷史快照。這可以用來防止敏感資料進入 localStorage 快取,這對於共用/公用電腦可能很重要。歷史導航將如預期運作,但在還原時,將會從伺服器請求該 URL,而不是從本機歷史快取。
Htmx 期望它發出的 AJAX 請求的回應為 HTML,通常是 HTML 片段(儘管完整 HTML 文件搭配 hx-select 標籤也很有用)。然後,htmx 會將傳回的 HTML 按照指定的目標位置和交換策略交換到文件中。
有時您可能希望在交換中不做任何事情,但仍然可能會觸發客戶端事件(請參閱下文)。
對於這種情況,預設情況下,您可以傳回 204 - No Content
回應碼,並且 htmx 將忽略回應的內容。
如果伺服器傳回錯誤回應(例如 404 或 501),htmx 將會觸發 htmx:responseError
事件,您可以處理該事件。
如果發生連線錯誤,將會觸發 htmx:sendError
事件。
您可以透過修改或取代 htmx.config.responseHandling
陣列來配置上述 htmx 的行為。此物件是 JavaScript 物件的集合,定義如下
responseHandling: [
{code:"204", swap: false}, // 204 - No Content by default does nothing, but is not an error
{code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped
{code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors
{code:"...", swap: false} // catch all for any other response code
]
當 htmx 收到回應時,它會依序疊代 htmx.config.responseHandling
陣列,並測試給定物件的 code
屬性(當作正規表示式處理時)是否與目前回應相符。如果某個項目與目前回應碼相符,則會使用它來判斷是否以及如何處理回應。
此陣列中項目的回應處理配置可用的欄位為
code
- 代表將針對回應碼測試的正規表示式的字串。swap
- 如果應將回應交換到 DOM 中則為 true
,否則為 false
error
- 如果 htmx 應將此回應視為錯誤則為 true
ignoreTitle
- 如果 htmx 應忽略回應中的 title 標籤則為 true
select
- 用於從回應中選取內容的 CSS 選取器target
- 指定回應替代目標的 CSS 選取器swapOverride
- 回應的替代交換機制作為如何使用此配置的一個範例,請考慮伺服器端框架在發生驗證錯誤時,傳回 422 - Unprocessable Entity
回應的情況。預設情況下,htmx 將忽略該回應,因為它符合正規表示式 [45]..
。
使用 meta config 機制來配置 responseHandling,我們可以新增以下配置
<!--
* 204 No Content by default does nothing, but is not an error
* 2xx, 3xx and 422 responses are non-errors and are swapped
* 4xx & 5xx responses are not swapped and are errors
* all other responses are swapped using "..." as a catch-all
-->
<meta
name="htmx-config"
content='{
"responseHandling":[
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": true}
]
}'
/>
如果您想要交換所有內容,無論 HTTP 回應碼如何,您可以使用此配置
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--all responses are swapped-->
最後,值得考慮使用 回應目標擴充功能,該功能可讓您透過屬性宣告式地配置回應碼的行為。
在跨來源環境中使用 htmx 時,請記得配置您的網頁伺服器以設定 Access-Control 標頭,以便讓 htmx 標頭在用戶端可見。
htmx 在請求中包含許多有用的標頭
標頭 | 說明 |
---|---|
HX-Boosted | 表示請求是透過使用 hx-boost 的元素發出的 |
HX-Current-URL | 瀏覽器目前的 URL |
HX-History-Restore-Request | 如果請求是在本機歷史快取中遺失後進行歷史還原,則為「true」 |
HX-Prompt | 使用者對 hx-prompt 的回應 |
HX-Request | 始終為「true」 |
HX-Target | 如果目標元素存在,則為該元素的 id |
HX-Trigger-Name | 如果觸發元素存在,則為該元素的 name |
HX-Trigger | 如果觸發元素存在,則為該元素的 id |
htmx 支援一些特定於 htmx 的回應標頭
HX-Location
- 允許您執行不會進行完整頁面重新載入的用戶端重新導向HX-Push-Url
- 將新的 URL 推送到歷史堆疊中HX-Redirect
- 可以用來執行用戶端重新導向到新的位置HX-Refresh
- 如果設定為「true」,則用戶端將會對頁面執行完整重新整理HX-Replace-Url
- 取代位置列中的目前 URLHX-Reswap
- 允許您指定回應的交換方式。有關可能的值,請參閱 hx-swapHX-Retarget
- CSS 選取器,可將內容更新的目標更新為頁面上不同的元素HX-Reselect
- CSS 選取器,可讓您選擇要交換的響應部分。覆寫觸發元素上現有的 hx-select
HX-Trigger
- 允許您觸發用戶端事件HX-Trigger-After-Settle
- 允許您在 settle 步驟之後觸發用戶端事件HX-Trigger-After-Swap
- 允許您在 swap 步驟之後觸發用戶端事件有關 HX-Trigger
標頭的詳細資訊,請參閱 HX-Trigger
回應標頭。
透過 htmx 提交表單的好處是不再需要 Post/Redirect/Get 模式。在伺服器上成功處理 POST 請求後,您不需要傳回 HTTP 302 (重新導向)。您可以直接傳回新的 HTML 片段。
此外,對於 3xx 重新導向回應碼(例如 HTTP 302 (重新導向)),不會將上述回應標頭提供給 htmx 進行處理。相反,瀏覽器會在內部攔截重新導向,並傳回重新導向 URL 的標頭和回應。在可能的情況下,請使用替代的回應碼(例如 200)來允許傳回這些回應標頭。
htmx 請求中的操作順序為
htmx-request
類別套用到適當的元素htmx-swapping
類別htmx-swapping
類別htmx-added
類別新增到每個新的內容片段htmx-settling
類別套用到目標htmx-settling
類別htmx-added
類別您可以使用 htmx-swapping
和 htmx-settling
類別來建立頁面之間的 CSS 轉換。
Htmx 與 HTML5 驗證 API 整合,如果可驗證的輸入無效,則不會發出表單請求。無論是 AJAX 請求還是 WebSocket 發送,都是如此。
Htmx 會在驗證時觸發事件,可用於掛鉤自訂驗證和錯誤處理。
htmx:validation:validate
- 在呼叫元素的 checkValidity()
方法之前呼叫。可用於新增自訂驗證邏輯。htmx:validation:failed
- 當 checkValidity()
回傳 false 時呼叫,表示輸入無效。htmx:validation:halted
- 當因驗證錯誤而未發出請求時呼叫。特定錯誤可以在 event.detail.errors
物件中找到。預設情況下,非表單元素在發出請求之前不會進行驗證,但您可以將 hx-validate
屬性設定為「true」來啟用驗證。
以下是一個輸入的範例,該輸入使用 hx-on
屬性來捕捉 htmx:validation:validate
事件,並要求輸入的值為 foo
。
<form id="example-form" hx-post="/test">
<input name="example"
onkeyup="this.setCustomValidity('') // reset the validation on keyup"
hx-on:htmx:validation:validate="if(this.value != 'foo') {
this.setCustomValidity('Please enter the value foo') // set the validation error
htmx.find('#example-form').reportValidity() // report the issue
}">
</form>
請注意,所有客戶端驗證都必須在伺服器端重新進行,因為它們總是可能被繞過。
Htmx 允許您僅使用 HTML 和 CSS 在許多情況下使用 CSS 轉場效果。
請參閱 動畫指南 以取得更多可用選項的詳細資訊。
htmx 提供一個 擴展機制,允許您自訂程式庫的行為。擴展 以 javascript 定義,然後透過 hx-ext
屬性啟用。
以下是如何安裝 idiomorph 擴展,該擴展允許您使用 Idiomorph DOM 變形演算法進行 htmx 交換。
<head>
<script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
</head>
<body hx-ext="morph">
...
<button hx-post="/example" hx-swap="morph" hx-target="#content">
Update Content
</button>
...
</body>
首先,包含擴展 (應該在包含 htmx.js
之後 包含),然後透過 hx-ext
屬性按名稱引用擴展。這使您可以使用 morph
交換。
htmx 支援一些由 htmx 開發團隊支援的「核心」擴展。
morph
交換策略404
)指定目標元素您可以在 擴展 頁面上查看所有可用的擴展。
如果您有興趣將自己的擴展新增至 htmx,請參閱擴展文件。
Htmx 具有廣泛的 事件機制,同時也兼具記錄系統的功能。
如果您想註冊給定的 htmx 事件,您可以使用
document.body.addEventListener('htmx:load', function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
或者,如果您願意,可以使用以下 htmx 輔助程式
htmx.on("htmx:load", function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
每次 htmx 將元素載入 DOM 時,都會觸發 htmx:load
事件,這實際上等同於正常的 load
事件。
htmx 事件的一些常見用途是
使用 htmx:load
事件初始化內容非常常見,因此 htmx 提供了一個輔助函式
htmx.onLoad(function(target) {
myJavascriptLib.init(target);
});
這與第一個範例的作用相同,但更簡潔一些。
您可以處理 htmx:configRequest
事件,以便在發出 AJAX 請求之前修改它
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the request
evt.detail.headers['Authentication-Token'] = getAuthToken(); // add a new header into the request
});
在這裡,我們在發送請求之前,向請求新增了一個參數和標頭。
您可以處理 htmx:beforeSwap
事件,以便修改 htmx 的交換行為
document.body.addEventListener('htmx:beforeSwap', function(evt) {
if(evt.detail.xhr.status === 404){
// alert the user when a 404 occurs (maybe use a nicer mechanism than alert())
alert("Error: Could Not Find Resource");
} else if(evt.detail.xhr.status === 422){
// allow 422 responses to swap as we are using this as a signal that
// a form was submitted with bad data and want to rerender with the
// errors
//
// set isError to false to avoid error logging in console
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if(evt.detail.xhr.status === 418){
// if the response code 418 (I'm a teapot) is returned, retarget the
// content of the response to the element with the id `teapot`
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
在這裡,我們處理一些 400 級錯誤回應代碼,這些代碼通常不會在 htmx 中進行交換。
請注意,所有事件都會以兩個不同的名稱觸發
因此,舉例來說,您可以監聽 htmx:afterSwap
或 htmx:after-swap
。這有助於與其他程式庫的互通性。例如,Alpine.js 需要烤肉串式命名。
如果您在 htmx.logger
設定記錄器,則每個事件都會被記錄。這對於疑難排解非常有用
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
使用 htmx(或任何其他宣告式語言)進行宣告式和事件驅動程式設計可能是一項精彩且高效的活動,但與命令式方法相比,一個缺點是它可能更難除錯。
例如,如果您不知道訣竅,就很難弄清楚為什麼某些事情沒有發生。
好吧,這裡有一些訣竅
您可以使用的第一個除錯工具是 htmx.logAll()
方法。這將記錄 htmx 觸發的每個事件,並允許您確切了解程式庫正在執行什麼操作。
htmx.logAll();
當然,這不會告訴您為什麼 htmx 沒有執行某些操作。您可能也不知道 DOM 元素觸發哪些事件來用作觸發器。為了解決這個問題,您可以使用瀏覽器控制台中的 monitorEvents()
方法
monitorEvents(htmx.find("#theElement"));
這將把 id 為 theElement
的元素上發生的所有事件輸出到控制台,並允許您確切了解它的狀況。
請注意,這僅適用於控制台,您無法將其嵌入到頁面上的腳本標籤中。
最後,如果逼不得已,您可能只想透過載入未縮小的版本來除錯 htmx.js
。它大約有 2500 行 javascript,因此程式碼量並不大。您很可能想在 issueAjaxRequest()
和 handleAjaxResponse()
方法中設定斷點,以了解發生了什麼事。
如果需要協助,也隨時可以加入 Discord。
有時,為了演示錯誤或澄清用法,最好能夠使用像 jsfiddle 這樣的 javascript 片段網站。為了方便建立演示,htmx 會託管一個演示腳本網站,該網站將安裝
只需將以下腳本標籤新增至您的演示/fiddle/任何內容
<script src="https://demo.htmx.org"></script>
這個輔助程式允許您透過新增具有 url
屬性來指示哪個 URL 的 template
標籤來新增模擬回應。該 URL 的回應將是範本的 innerHTML,使其易於建構模擬回應。您可以使用 delay
屬性向回應新增延遲,該屬性應為指示延遲毫秒數的整數
您可以使用 ${}
語法在範本中嵌入簡單的表達式。
請注意,這僅應用於演示,絕不保證長時間工作,因為它將始終獲取 htmx 和 hyperscript 的最新版本!
以下是程式碼實際運作的範例
<!-- load demo environment -->
<script src="https://demo.htmx.org"></script>
<!-- post to /foo -->
<button hx-post="/foo" hx-target="#result">
Count Up
</button>
<output id="result"></output>
<!-- respond to /foo with some dynamic content in a template tag -->
<script>
globalInt = 0;
</script>
<template url="/foo" delay="500"> <!-- note the url and delay attributes -->
${globalInt++}
</template>
儘管 htmx 鼓勵使用超媒體方法來建構 Web 應用程式,但它也為客戶端腳本提供了許多選項。腳本包含在 Web 架構的 REST 式描述中,請參閱:Code-On-Demand。在可行的範圍內,我們建議在您的 Web 應用程式中使用 超媒體友善的腳本方法
htmx 和腳本解決方案之間的主要整合點是 htmx 發送和可以回應的 事件。請參閱 第三方 Javascript 區段中的 SortableJS 範例,以了解透過事件將 JavaScript 程式庫與 htmx 整合的良好範本。
與 htmx 搭配使用的腳本解決方案包括
我們在我們的書中有一整章題為「客戶端腳本」的章節,探討如何將腳本整合到您基於 htmx 的應用程式中。
hx-on*
屬性HTML 允許透過 onevent
屬性(例如 onClick
)嵌入內聯腳本。
<button onclick="alert('You clicked me!')">
Click Me!
</button>
此功能允許將腳本邏輯與該邏輯應用到的 HTML 元素放在一起,提供良好的行為局部性 (Locality of Behaviour, LoB)。不幸的是,HTML 只允許對固定數量的特定 DOM 事件(例如 onclick
)使用 on*
屬性,並且沒有提供通用的機制來回應元素上的任意事件。
為了彌補這個缺點,htmx 提供了 hx-on*
屬性。這些屬性允許您以保留標準 on*
屬性的 LoB 的方式來回應任何事件。
如果我們想使用 hx-on
屬性來回應 click
事件,我們會這樣寫:
<button hx-on:click="alert('You clicked me!')">
Click Me!
</button>
也就是說,字串 hx-on
,後接冒號(或破折號),然後是事件的名稱。
當然,對於 click
事件,我們建議堅持使用標準的 onclick
屬性。但是,考慮一個由 htmx 驅動的按鈕,該按鈕希望使用 htmx:config-request
事件將參數添加到請求中。這無法使用標準的 on*
屬性來完成,但可以使用 hx-on:htmx:config-request
屬性來完成:
<button hx-post="/example"
hx-on:htmx:config-request="event.detail.parameters.example = 'Hello Scripting!'">
Post Me!
</button>
在這裡,example
參數會被添加到 POST
請求中,然後才發出,其值為「Hello Scripting!」。
hx-on*
屬性是一種非常簡單的通用嵌入式腳本機制。它不是取代更成熟的前端腳本解決方案(如 AlpineJS 或 hyperscript)。然而,它可以增強您基於 VanillaJS 的腳本方法,在您基於 htmx 的應用程式中。
請注意,HTML 屬性是不區分大小寫的。這意味著,遺憾的是,無法回應依賴於大寫/駝峰式命名法的事件。如果您需要支援駝峰式命名法的事件,我們建議使用功能更完整的腳本解決方案,例如 AlpineJS 或 hyperscript。htmx 出於這個原因,會以駝峰式命名法和烤肉串式命名法分派其所有事件。
Htmx 與第三方函式庫的整合相當良好。如果該函式庫在 DOM 上觸發事件,您可以使用這些事件來觸發來自 htmx 的請求。
一個很好的例子是 SortableJS 演示
<form class="sortable" hx-post="/items" hx-trigger="end">
<div class="htmx-indicator">Updating...</div>
<div><input type='hidden' name='item' value='1'/>Item 1</div>
<div><input type='hidden' name='item' value='2'/>Item 2</div>
<div><input type='hidden' name='item' value='2'/>Item 3</div>
</form>
與大多數 javascript 函式庫一樣,您需要在某個時間點初始化 Sortable 的內容。
在 jquery 中,您可能會這樣做:
$(document).ready(function() {
var sortables = document.body.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
});
在 htmx 中,您會改用 htmx.onLoad
函式,並且只會從新載入的內容中選擇,而不是整個文件:
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
})
這將確保當 htmx 將新內容添加到 DOM 時,排序元素會被正確初始化。
如果 javascript 將具有 htmx 屬性的內容添加到 DOM,您需要確保使用 htmx.process()
函式初始化此內容。
例如,如果您要使用 fetch
API 擷取一些資料並將其放入 div 中,並且該 HTML 中包含 htmx 屬性,則您需要像這樣新增對 htmx.process()
的呼叫:
let myDiv = document.getElementById('my-div')
fetch('http://example.com/movies.json')
.then(response => response.text())
.then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } );
某些第三方函式庫會從 HTML 範本元素建立內容。例如,Alpine JS 會在範本上使用 x-if
屬性來有條件地新增內容。這些範本最初不屬於 DOM 的一部分,如果它們包含 htmx 屬性,則需要在載入後呼叫 htmx.process()
。以下範例使用 Alpine 的 $watch
函式來尋找會觸發條件內容的值變更:
<div x-data="{show_new: false}"
x-init="$watch('show_new', value => {
if (show_new) {
htmx.process(document.querySelector('#new_content'))
}
})">
<button @click = "show_new = !show_new">Toggle New Content</button>
<template x-if="show_new">
<div id="new_content">
<a hx-get="/server/newstuff" href="#">New Clickable</a>
</div>
</template>
</div>
請參閱 Web Components 範例頁面,以取得有關如何將 htmx 與 Web Component 整合的範例。
htmx 可以直接使用標準的 HTTP 快取機制。
如果您的伺服器將 Last-Modified
HTTP 回應標頭添加到指定 URL 的回應中,則瀏覽器會自動將 If-Modified-Since
請求 HTTP 標頭添加到對同一 URL 的下一個請求中。請注意,如果您的伺服器可以根據其他標頭為同一個 URL 呈現不同的內容,則您需要使用 Vary
回應 HTTP 標頭。例如,如果您的伺服器在缺少 HX-Request
標頭或 false
時呈現完整的 HTML,並且在 HX-Request: true
時呈現該 HTML 的片段,則您需要新增 Vary: HX-Request
。這會使快取基於回應 URL 和 HX-Request
請求標頭的組合鍵 — 而不是僅基於回應 URL。
如果您無法(或不願意)使用 Vary
標頭,您可以選擇將組態參數 getCacheBusterParam
設定為 true
。如果設定了此組態變數,htmx 會在其發出的 GET
請求中包含一個快取清除參數,這將防止瀏覽器在同一個快取槽中快取基於 htmx 和非 htmx 的回應。
htmx 也會按預期方式與 ETag
搭配使用。請注意,如果您的伺服器可以為同一個 URL 呈現不同的內容(例如,根據 HX-Request
標頭的值),則伺服器需要為每個內容產生不同的 ETag
。
htmx 允許您直接在 DOM 中定義邏輯。這有許多優點,其中最大的優點是行為局部性,這會使您的系統更容易理解和維護。
但是,這種方法的一個問題是安全性:由於 htmx 提高了 HTML 的表達能力,如果惡意使用者能夠將 HTML 注入到您的應用程式中,他們可以利用 htmx 的這種表達能力來達到惡意的目的。
基於 HTML 的 Web 開發的第一條規則一直是:不要信任來自使用者的輸入。您應該逸出所有注入到您網站的第三方、不受信任的內容。這是為了防止(除其他問題外)XSS 攻擊。
在出色的 OWASP 網站上有關於 XSS 以及如何防止它的廣泛文件,包括跨網站腳本防護速查表。
好消息是,這是一個非常古老且廣為人知的主題,並且絕大多數伺服器端範本語言都支援內容的自動逸出,以防止出現此類問題。
話雖如此,有時人們會選擇以更危險的方式注入 HTML,通常是透過其範本語言中的某種 raw()
機制。這樣做可能有充分的理由,但是如果注入的內容來自第三方,則必須清除該內容,包括刪除以 hx-
和 data-hx
開頭的屬性,以及內聯 <script>
標籤等。
如果您要注入原始 HTML 並自行逸出,最佳實務是白名單您允許的屬性和標籤,而不是黑名單您不允許的屬性和標籤。
當然,錯誤會發生,開發人員也不是完美的,因此最好對您的 Web 應用程式採取分層的安全方法,並且 htmx 提供工具來協助保護您的應用程式。
讓我們來看一看它們。
hx-disable
htmx 提供的第一個有助於進一步保護您的應用程式的工具是 hx-disable
屬性。此屬性將阻止處理給定元素以及其中所有元素上的所有 htmx 屬性。因此,例如,如果您要在範本中包含原始 HTML 內容(再次說明,不建議這樣做!),則您可以在內容周圍放置一個 div,並在其上使用 hx-disable
屬性:
<div hx-disable>
<%= raw(user_content) %>
</div>
htmx 將不會處理該內容中找到的任何與 htmx 相關的屬性或功能。無法透過注入其他內容來停用此屬性:如果在元素的父階層中的任何位置找到 hx-disable
屬性,則 htmx 將不會處理該元素。
hx-history
另一個安全考量是 htmx 歷史記錄快取。您可能有一些頁面具有您不希望儲存在使用者 localStorage
快取中的敏感資料。您可以透過在頁面上任何位置包含 hx-history
屬性,並將其值設定為 false
,來從歷史記錄快取中省略給定的頁面。
htmx 還提供了與安全性相關的組態選項:
htmx.config.selfRequestsOnly
- 如果設定為 true
,則只允許對與目前文件相同網域的請求htmx.config.allowScriptTags
- htmx 會處理在其載入的新內容中找到的 <script>
標籤。如果您想停用此行為,您可以將此組態變數設定為 false
htmx.config.historyCacheSize
- 可以設定為 0
,以避免在 localStorage
快取中儲存任何 HTMLhtmx.config.allowEval
- 可以設定為 false
,以停用 htmx 所有依賴 eval 的功能hx-on:
屬性js:
前綴的 hx-vals
js:
前綴的 hx-headers
請注意,透過停用 eval()
而移除的所有功能都可以使用您自己的自訂 javascript 和 htmx 事件模型來重新實作。
如果您想允許對目前主機以外的某些網域發出請求,但又不希望完全開放,您可以使用 htmx:validateUrl
事件。此事件會在 detail.url
插槽中提供請求 URL,以及 sameHost
屬性。
您可以檢查這些值,如果請求無效,請在事件上呼叫 preventDefault()
以防止發出請求。
document.body.addEventListener('htmx:validateUrl', function (evt) {
// only allow requests to the current server as well as myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
瀏覽器還提供了用於進一步保護您的 Web 應用程式的工具。可用的最強大工具是內容安全性原則 (Content Security Policy, CSP)。使用 CSP,您可以告訴瀏覽器(例如)不要向非來源主機發出請求,不要評估內聯腳本標籤等。
以下是一個 meta
標籤中的 CSP 範例:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
這會告訴瀏覽器「只允許連線到原始(來源)網域」。這與 htmx.config.selfRequestsOnly
會有重複,但處理應用程式安全時,採用分層式的安全方法是必要的,而且實際上是理想的。
關於 CSP 的完整討論超出本文檔的範圍,但 MDN 文章 提供了一個很好的起點,可供您探索此主題。
htmx 有一些可以透過程式碼或宣告方式存取的設定選項。它們列在下方
設定變數 | 資訊 |
---|---|
htmx.config.historyEnabled | 預設為 true ,實際上只對測試有用 |
htmx.config.historyCacheSize | 預設為 10 |
htmx.config.refreshOnHistoryMiss | 預設為 false ,如果設定為 true ,htmx 會在歷史記錄遺失時發出完整的頁面重新整理,而不是使用 AJAX 請求 |
htmx.config.defaultSwapStyle | 預設為 innerHTML |
htmx.config.defaultSwapDelay | 預設為 0 |
htmx.config.defaultSettleDelay | 預設為 20 |
htmx.config.includeIndicatorStyles | 預設為 true (決定是否載入指示器樣式) |
htmx.config.indicatorClass | 預設為 htmx-indicator |
htmx.config.requestClass | 預設為 htmx-request |
htmx.config.addedClass | 預設為 htmx-added |
htmx.config.settlingClass | 預設為 htmx-settling |
htmx.config.swappingClass | 預設為 htmx-swapping |
htmx.config.allowEval | 預設為 true ,可用於停用 htmx 對某些功能(例如觸發器篩選器)的 eval 使用 |
htmx.config.allowScriptTags | 預設為 true ,決定 htmx 是否會處理在新內容中找到的 script 標籤 |
htmx.config.inlineScriptNonce | 預設為 '' ,表示不會將 nonce 新增至內嵌腳本 |
htmx.config.attributesToSettle | 預設為 ["class", "style", "width", "height"] ,在 settling 階段要處理的屬性 |
htmx.config.inlineStyleNonce | 預設為 '' ,表示不會將 nonce 新增至內嵌樣式 |
htmx.config.useTemplateFragments | 預設為 false ,用於解析來自伺服器的內容的 HTML template 標籤(不與 IE11 相容!) |
htmx.config.wsReconnectDelay | 預設為 full-jitter |
htmx.config.wsBinaryType | 預設為 blob ,透過 WebSocket 連線接收的二進位資料類型 |
htmx.config.disableSelector | 預設為 [hx-disable], [data-hx-disable] ,htmx 不會處理具有此屬性或其父項的元素 |
htmx.config.withCredentials | 預設為 false ,允許使用 Cookie、授權標頭或 TLS 用戶端憑證等憑證進行跨站存取控制請求 |
htmx.config.timeout | 預設為 0,請求在自動終止之前可以花費的毫秒數 |
htmx.config.scrollBehavior | 預設為 'instant',使用帶有 hx-swap 的 show 修飾符時的滾動行為。允許的值為 instant (滾動應立即發生,單次跳躍)、smooth (滾動應平滑地動畫化)和 auto (滾動行為由 scroll-behavior 的計算值決定)。 |
htmx.config.defaultFocusScroll | 是否應將焦點元素滾動到視圖中,預設為 false,可以使用 focus-scroll swap 修飾符覆寫。 |
htmx.config.getCacheBusterParam | 預設為 false,如果設定為 true,htmx 會將目標元素以 org.htmx.cache-buster=targetElementId 的格式附加到 GET 請求中 |
htmx.config.globalViewTransitions | 如果設定為 true ,htmx 將在交換新內容時使用 View Transition API。 |
htmx.config.methodsThatUseUrlParams | 預設為 ["get", "delete"] ,htmx 將透過將這些方法的參數編碼在 URL 中而不是請求本文中來格式化請求 |
htmx.config.selfRequestsOnly | 預設為 true ,是否只允許 AJAX 請求連線到與目前文件相同的網域 |
htmx.config.ignoreTitle | 預設為 false ,如果設定為 true ,htmx 在新內容中找到 title 標籤時不會更新文件的標題 |
htmx.config.disableInheritance | 停用 htmx 中的屬性繼承,然後可以使用 hx-inherit 屬性覆寫 |
htmx.config.scrollIntoViewOnBoost | 預設為 true ,是否將 boost 元素的目標滾動到視窗中。如果在 boost 元素上省略 hx-target ,則目標預設為 body ,導致頁面滾動到頂部。 |
htmx.config.triggerSpecsCache | 預設為 null ,用於儲存已評估的觸發器規格的快取,以提高剖析效能,但會增加記憶體使用量。您可以定義一個簡單的物件來使用永不清除的快取,或使用 Proxy 物件來實作您自己的系統 |
htmx.config.responseHandling | 可以在此處設定回應狀態碼的預設 回應處理行為,以進行交換或錯誤 |
htmx.config.allowNestedOobSwaps | 預設為 true ,是否處理主要回應元素內巢狀元素的 OOB 交換。請參閱巢狀 OOB 交換。 |
您可以直接在 javascript 中設定它們,也可以使用 meta
標籤
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
就是這樣!
祝您使用 htmx 玩得開心!您可以不用寫太多程式碼就完成相當多的工作!