This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Friday, 28 November 2025
如果您已经建立传统的 ASP.NET 核心 MVC 应用程序, 您就会知道问题所在: 当用户在页面之间浏览时害怕“ 点击闪光 ” 。 全页重新加载, 浏览器铬闪烁, 内容随新页面的转换而跳动。 它有效, 但是没有效果 。 感觉感觉 现代的。
从服务器上返回部分 HTML 碎片并将其转换为 DOM 的方法解决了这个问题,这不是新办法。自从jQuery 几天以来,我一直在使用这种模式,甚至在那之前,用Vanilla JavaScript和Vanilla JavaScript XMLHttpRequest改变的是 优雅 它变成HTMX。
HTMX给了我们一种宣示性的方式来做我们一直做的事: 返回服务器发送的 HTML 并将其转换为页面。 不再为每次互动写入自定义 JavaScript 。 不再在“ 适当” 服务器侧面开发与平滑的用户体验之间做出选择 。 在 HTMX 中, 我们得到两者 。
随行条款: 本篇文章的重点是ASP.NET核心整合方面。关于深入潜入HTMX事件、生命周期和定制扩展,见我的配套文章: HTMX扩展和与 ASP.NET 核心使用HTMX.
在本篇文章中,我将向大家展示HTMX是如何与 ASP.NET核心部分 完美地融合在一起的。 HTMX.NET 图书馆让它变得更好, 如何我的 大部分是混杂的。 pagging 帮工 NuGet 软件包使用 HTMX 提供强大的射程,配置最小。
这个方法也是框架- 不可知性 — Django在版本6. 0 中添加了模板碎片, 铁路有涡轮框架, 而更广泛的网络生态系统正在包含 HTML over- the wire 模式。 这是一个建立服务器改造应用程序的好时机 。
HTMX 是一个允许您直接从 HTML 访问现代浏览器功能而不是写入 JavaScript 的图书馆。 它扩展了 HTML 属性, 允许您提出 AJAX 请求, 交换内容, 并创建丰富的互动关系 - 全部不留下标记 。
您最常使用的密钥属性 :
hx-get, hx-post, hx-put, hx-delete - 提出HTTP请求hx-target - 具体说明将答复放在何处hx-swap - 控制内容交换方式(内置HTML、外置HTML等)hx-trigger - 界定什么触发请求(单击、修改、装载等)hx-push-url - 更新浏览器 URL, 不重新装入整页这就是它的美丽之处: 你仍然在写服务器的代码, 返回服务器发送的 HTML 。 没有 JSON APIS, 没有客户端模板, 没有管道建设。 只是在电线上有很好的旧式 HTML 。
在HTMX之前,我们通过举行更多的仪式取得了同样的效果。这就是在jQuery时代部分更新的情况:
// jQuery circa 2010
$('#load-more').click(function() {
$.ajax({
url: '/posts/page/' + currentPage,
success: function(html) {
$('#posts-container').append(html);
currentPage++;
}
});
});
更早以前,与香草 JavaScript:
// Vanilla JS circa 2005
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
document.getElementById('content').innerHTML = xhr.responseText;
}
};
xhr.open('GET', '/partial-content', true);
xhr.send();
服务器的侧面模式是相同的 - 返回 HTML 碎片, 将其转换为 DOM 。 HTMX 只是将这一逻辑从 JavaScript 移动到 HTML 属性, 使得它具有宣示性、 可发现性, 并且更不易出错。 创新不是技术, 而是界面 。
ASP.NET开发商已经这样做多年了。 PartialView() 从第一天起,在MVC, Html.RenderAction() 我们所缺少的是一种优雅的、标准化的方法 将它连接到客户的侧面。HTMX完美地填补了这个空白。
更广大的产业也在重新发现这些模式。 “ SSR” (Server-Side Rendering) 、 “ 混合转换 ” 和“ 岛屿结构 ” 等术语在本质上描述了服务器边框的一贯作用, 但用新鲜的眼光。 它的验证是服务器接收的方法尺度、 运行以及 — — 使用正确的工具 — — 提供了极好的用户经验。
首先,将 HTMX 包含在您的布局中。 您可以使用 CDN 或本地服务 :
<script src="https://unpkg.com/[email protected]"></script>
就是这样,没有建筑台阶,没有Npm安装,没有网页包配置,只要放下一个脚本标签,你就可以去比赛了。
对于客户端反应(显示/隐藏元素、州、地方界面状态), 阿尔卑山 完全补充 HTMX 。 仅15KB gzipped 时, 它提供Vue/ React 类似声明性反应, 没有浮肿 。
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
以下是他们如何合作:
<div x-data="{ open: false }">
<button x-on:click="open = !open">Toggle</button>
<div x-show="open" x-transition>
<button hx-get="/api/data" hx-target="#results">Load Data</button>
</div>
</div>
Alpine 处理本地 UI( tggle) , HTMX 处理服务器呼叫( 获取数据) 。 在本篇文章中, 您可以看到这个模式 - Alpine 用于客户端反应, HTMX 用于服务器互动 。
哈立德·阿布哈克梅数(_S) HTMX.NET 图书馆提供头等 ASP.NET 核心集成。 Htmx 和 Htmx.TagHelpers NuGet 软件包, 它感觉自己是. NET 的原型, 并且让HTMX的工作 成为绝对的乐趣。 你会发现更多关于Khalid 的绝佳开放源码工作 吉特胡布.
dotnet add package Htmx
dotnet add package Htmx.TagHelpers
在你的 _ViewImports.cshtml:
@addTagHelper *, Htmx.TagHelpers
最有用的特征是: Request.IsHtmx() 扩展方法, 它告诉您请求是否来自 HTMX 。 这样您就可以返回完整视图或部分视图 :
[HttpGet]
public async Task<IActionResult> Index(int page = 1, int pageSize = 20)
{
var posts = await blogViewService.GetPagedPosts(page, pageSize);
if (Request.IsHtmx())
return PartialView("_BlogSummaryList", posts);
return View("Index", posts);
}
这个模式绝对是辉煌的。 单一控制器的动作对以下两方面都起作用:
没有单独的 API 端点, 没有重复逻辑, 没有 JSON 序列管理 。
一种常见的误解: 许多ASP.NET开发商认为你需要
_前缀( 类似)_BlogSummaryList.cshtml来做部分翻译,你没有!PartialView()方法本身让 ASP. NET Core 跳过布局, 它的意思是“ 忘记布局, 只做这个位子。” 您可以使用return PartialView("SearchResults", model)带有常规视图文件, 并且运作完美。 突出的公约起源于 ASP. NET WebPages (WebMatrix) , 它防止文件直接通过 URL 直接送达 URL - 但监查中心总是保护所有观点不被直接访问 。 它纯粹是帮助识别观点的命名公约 。 预定 作为部分。
HTMX. NET 提供标签辅助器, 使控制器更清洁。 您可以使用强型参考符, 而不是写入路径字符串 :
<button
hx-controller="Comment"
hx-action="GetCommentForm"
hx-post
hx-target="#commentform">
Reply
</button>
这将使用 ASP. NET Core 的路径系统生成正确的路径。 如果您重命名您的控制器或动作, 您的 IDE 将会抓住它。 比魔法字符串要好得多 !
以下是博客评论系统的一个真实例子:
<button
class="btn btn-outline btn-sm mb-4"
hx-action="Comment"
hx-controller="Comment"
hx-post
hx-vals
x-on:click.prevent="window.mostlylucid.comments.setValues($event)"
hx-on="htmx:afterSwap: window.scrollTo({top: 0, behavior: 'smooth'})"
hx-swap="outerHTML"
hx-target="#commentform">
Comment
</button>
请注意HTMX如何与Alpinejs(Alpinejs)很好相处(HTMX)。x-on:click.prevent对于那些偶尔的客户端互动部分。
图书馆还提供:
Request.IsHtmxNonBoosted() - 检查是否是HTMX要求,但没有提升Request.IsHtmxRefresh() - 检查是否是历史恢复请求以下是这个博客的搜索控制器, 显示三层返回模式:
[HttpGet]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]
public async Task<IActionResult> Search(
string? query,
int page = 1,
int pageSize = 10,
[FromHeader] bool pagerequest = false)
{
var searchModel = await BuildSearchModel(query, page, pageSize);
if (pagerequest && Request.IsHtmx())
return PartialView("_SearchResultsPartial", searchModel.SearchResults);
if (Request.IsHtmx())
return PartialView("SearchResults", searchModel);
return View("SearchResults", searchModel);
}
三种假设情景的三个返回路径:
部分意见(_SearchResultsPartial.cshtml使用 Page 标签助手 :
@model Mostlylucid.Models.Blog.PostListViewModel
<div class="pt-2" id="content">
@if (Model.Data?.Any() is true)
{
<div class="inline-flex w-full items-center justify-center pb-4">
@if (Model.TotalItems > Model.PageSize)
{
<pager
x-ref="pager"
link-url="@Model.LinkUrl"
hx-boost="true"
hx-target="#content"
hx-swap="show:none"
page="@Model.Page"
page-size="@Model.PageSize"
total-items="@Model.TotalItems"
hx-headers='{"pagerequest": "true"}'>
</pager>
}
</div>
@foreach (var post in Model.Data)
{
<partial name="_ListPost" model="post"/>
}
}
</div>
分解呼叫器标签助手 :
hx-boost="true" - 拦截链接,转换为AJAXhx-target="#content" - 向何处注射答复hx-headers='{"pagerequest": "true"}' - 自定义信头告诉控制器它的射速Request.IsHtmx() && pagerequest 仅返回最小部分我写过 大部分是混杂的。 pagging 帮工 这是HTMX第一,但没有JavaScript也能工作。
dotnet add package mostlylucid.pagingtaghelper
添加到 _ViewImports.cshtml:
@addTagHelper *, mostlylucid.pagingtaghelper
执行 实施 IPagingModel<T> 然后你们就完了:
public class BasePagingModel<T> : IPagingModel<T> where T : class
{
public int Page { get; set; }
public int TotalItems { get; set; }
public int PageSize { get; set; }
public string LinkUrl { get; set; }
public List<T> Data { get; set; }
}
你会得到什么:
标签助手生成链接, 以保存查询字符串, 支持自定义信头, 并天衣无缝地与 HTMX 整合( 见上文示例) 。
以下是整个系统是如何结合的:
graph TB
A[Browser] -->|"Initial page load"| B[Controller Action]
B -->|"Request.IsHtmx() = false"| C[Return Full View]
C --> D[Render Layout + Partial]
A -->|"User clicks pagination/filter"| E[HTMX Request]
E -->|"hx-get with headers"| F[Same Controller Action]
F -->|"Request.IsHtmx() = true"| G{Check Headers}
G -->|"pagerequest header"| H[Return Minimal Partial]
G -->|"No special header"| I[Return Section Partial]
H --> J[Swap Content in Target]
I --> J
J -->|"User clicks another link"| E
style B stroke:#333,stroke-width:2px
style F stroke:#333,stroke-width:2px
style C stroke:#0066cc,stroke-width:2px
style H stroke:#00cc66,stroke-width:2px
style I stroke:#00cc66,stroke-width:2px
Django在6.0版(2024年12月)中添加了适当的部分模板,并附有模板碎片。在此之前,Django开发商通常使用包容标签或第三方包,如django-render-block。 ASP.NET Core曾经使用过。 ASP.NET Core PartialView() 自2016年1.0版以来 -- -- 不同的框架、不同的时间表,但同一目的地:HTMX的HTML碎片。
铁路上的Ruby有Turbo框架(热电线的一部分),
<%= turbo_frame_tag "posts" do %>
<%= render @posts %>
<% end %>
不同之处在于涡轮在请求和响应时都需要具体的框架标记。 HTMX 比较灵活 - 任何终点都可以返回任何 HTML, 然后由您决定它与 HTML 的去向 。 hx-target.
Elixir的“凤凰生活观察”采用不同的方法,
def handle_event("load_more", _params, socket) do
{:noreply, assign(socket, posts: load_more_posts())}
end
LiveView对实时应用程序来说非常出色,但它需要 WebSocket 基础设施以及连接服务器的服务器内存。 HTMX 使用普通的旧 HTTP - 无国籍、可缓存、可缩放。 对于博客来说,这是完美的。
输出缓存: 变化: OutputCache 属性因 hx-request 标题, 缓存整页和部分部分, 分别 :
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]
网络效率:服务器发送的 HTML 往往小于 JSON + 客户端模板,需要的往返旅行减少,并适当隐藏缓存。
套套装大小: HTMX (14KB) + 可选阿尔卑斯山(15KB) + 呼叫标签助手 (0KB, 服务器侧) = 低于 30KB 总计。 与典型反应应用程序 (200KB+) 相比 。
乐观的用户界面更新 - 结合HTMX和阿尔卑斯山,以获得即时反馈:
<div x-data="{ count: @Model.CommentCount }">
<button hx-post="/comment/like" x-on:click="count++" hx-on::after-request="count = $event.detail.xhr.response">
Likes: <span x-text="count"></span>
</button>
</div>
立即更新计数( 乐观) , 然后与服务器响应同步 。
网外交换 - 从一个答复中更新多页章节:
<div id="main-content"><!-- Main response --></div>
<div id="notification-count" hx-swap-oob="true"><span>5 new</span></div>
完全适合通知徽章、车计数等。 使用 HTMX 显示通吐和互换.
带有WebAPIPI的客户-用户版模板 - 有时你想要一个中途体验: 服务器发送的 HTML 用于大多数事物, 但来自WebAPI的JSON 用于特定的动态内容。 HTMX 客户端侧边模板扩展 允许您这样做 :
<script src="https://unpkg.com/[email protected]/client-side-templates.js"></script>
<script src="https://unpkg.com/mustache@latest"></script>
<div hx-ext="client-side-templates">
<template id="post-template" type="text/mustache">
{{#posts}}
<div class="post">
<h3>{{title}}</h3>
<p>{{excerpt}}</p>
</div>
{{/posts}}
</template>
<button hx-get="/api/posts"
hx-target="#post-list"
mustache-template="post-template">
Load Posts
</button>
<div id="post-list"></div>
</div>
此方法与胡子、 Handlebars 或 Nunjucks 模板合作。 您的 WebAPI 返回 JSON, 但是 HTMX 处理客户端的转换。 当您已有 API 或需要与移动应用程序共享数据时, 此方法特别有用 。 有关 HTMX 扩展的更多信息, 包括客户端模板, 见 随带的物品,.
调试工具 : 安装 HTMX 调试器扩展扩展 - 它显示每一个请求,回应, 和交换实时。
ASP.NET Core的抗伪造符号需要与AJAX. HTMX.NET进行特殊处理。 HTMX.NET提供几种清洁选择。 HtmxAntiforgeryScriptEndpoint:
// In Program.cs
app.MapHtmxAntiforgeryScript();
<!-- In your layout head -->
<script src="@HtmxAntiforgeryScriptEndpoints.Path" defer></script>
或者使用标签助手 : <meta name="htmx-config" includeAspNetAntiforgeryToken="true" />见 Khalid关于HTMX反伪造托肯斯的文章 用于完整细节。
阿尔卑斯山 @click 与Razor的短手冲突 @ 语法。 使用明确的表达式代替 :
<button x-on:click="doSomething()">Click me</button> <!-- Instead of @click -->
<div x-bind:class="isOpen ? 'block' : 'hidden'"></div> <!-- Instead of :class -->
或与 @@click,但明确的语法更明确。
控制历史条目 : 默认情况下, HTMX 将每一个请求都推向历史。 对于不想要历史污染的页码/过滤器 :
<paging model="@Model" hx-push-url="false"> <!-- Don't add history entries -->
或使用 hx-replace-url="true" 更新 URL 而不添加条目。
幽灵部分问题: 后向/ 前向浏览器只显示一个没有布局的部分碎片。 hx-history-elt 容器内容:
<div class="container mx-auto" id="contentcontainer" hx-history-elt>
@RenderBody()
</div>
这告诉 HTMX 要截图哪个元素, 在恢复历史时保存周围的布局 。
症状: HTMX在本地工作,但打破了CDN的背后,错误的内容被缓存。
根原因 : CDN 忽略 HX-Request 头页。 您的服务器返回基于此页头的不同内容, 但 CDN 以相同的方式缓存 。
ASP.NET 核心修正: 使用使用 VaryByHeaderNames:
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" })]
CDN 修正 : 要包含的配置缓存规则 HX-Request 在缓存密钥中。 对于云雾 : Dashboard 自定义缓存密钥 HX-Request.
hx-boost 将正态链接转换为 AJAX 请求。 HTMX 奇格页面 指出一些核心小组成员建议避免(空缺) <head> 影响行为发生地),而其他人则认为这对速赢有利。
<div hx-boost="true" hx-target="#contentcontainer">
<a asp-action="Show" asp-route-slug="@post.Slug">Read More</a>
</div>
这个博客广泛使用它。 hx-get 更清楚,但 hx-boost 如果你坚持的话,可以工作。有选择地禁用 hx-boost="false" 关于儿童要素。
带有 ASP. NET 核心部分的 HTMX 表示在不牺牲现代 UX 的情况下, 返回到服务器端的简单化 。 您得到 :
服务器改造方法经受了时间的考验,HTMX终于拥有了它应得的优雅的客户端工具。你可以利用已经工作了几十年的模式构建强大的、有表现的网络应用程序 — — 它们只是等待正确的工具来再次发光。
本篇文章是HTMX和ASP.NET Core的两部分系列的一部分:
正式文件:
图书馆和工具 :
社区资源:
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.