Back to "带有ASP.net核心部分的HTMXHTMX:服务器-Side文艺复兴"

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

ASP.NET Core HTMX HTMX.NET Web Development

带有ASP.net核心部分的HTMXHTMX:服务器-Side文艺复兴

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?

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 。

和AJAX一样老的模式

在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) 、 “ 混合转换 ” 和“ 岛屿结构 ” 等术语在本质上描述了服务器边框的一贯作用, 但用新鲜的眼光。 它的验证是服务器接收的方法尺度、 运行以及 — — 使用正确的工具 — — 提供了极好的用户经验。

在 ASP.NET 核心中设置 HTMX

首先,将 HTMX 包含在您的布局中。 您可以使用 CDN 或本地服务 :

<script src="https://unpkg.com/[email protected]"></script>

就是这样,没有建筑台阶,没有Npm安装,没有网页包配置,只要放下一个脚本标签,你就可以去比赛了。

Alpine.js:客户辅助部分

对于客户端反应(显示/隐藏元素、州、地方界面状态), 阿尔卑山 完全补充 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 用于服务器互动 。

HTMX.NET图书馆

哈立德·阿布哈克梅数(_S) HTMX.NET 图书馆提供头等 ASP.NET 核心集成。 HtmxHtmx.TagHelpers NuGet 软件包, 它感觉自己是. NET 的原型, 并且让HTMX的工作 成为绝对的乐趣。 你会发现更多关于Khalid 的绝佳开放源码工作 吉特胡布.

安装安装

dotnet add package Htmx
dotnet add package Htmx.TagHelpers

在你的 _ViewImports.cshtml:

@addTagHelper *, Htmx.TagHelpers

IsHtmx () 扩展方法

最有用的特征是: 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);
}

这个模式绝对是辉煌的。 单一控制器的动作对以下两方面都起作用:

  • 全页负载(当用户直接导航或刷新时)
  • 部分更新(当HTMX提出要求时)

没有单独的 API 端点, 没有重复逻辑, 没有 JSON 序列管理 。

一种常见的误解: 许多ASP.NET开发商认为你需要 _ 前缀( 类似) _BlogSummaryList.cshtml来做部分翻译,你没有! PartialView() 方法本身让 ASP. NET Core 跳过布局, 它的意思是“ 忘记布局, 只做这个位子。” 您可以使用 return PartialView("SearchResults", model) 带有常规视图文件, 并且运作完美。 突出的公约起源于 ASP. NET WebPages (WebMatrix) , 它防止文件直接通过 URL 直接送达 URL - 但监查中心总是保护所有观点不被直接访问 。 它纯粹是帮助识别观点的命名公约 。 预定 作为部分。

HTMX.net 标记辅助工具

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对于那些偶尔的客户端互动部分。

其他HTMX.net辅助人员

图书馆还提供:

  • Request.IsHtmxNonBoosted() - 检查是否是HTMX要求,但没有提升
  • Request.IsHtmxRefresh() - 检查是否是历史恢复请求
  • HTMX 信头( 触发、 重定向等) 的响应助手

真实世界示例:部分搜索

以下是这个博客的搜索控制器, 显示三层返回模式:

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

三种假设情景的三个返回路径:

  1. 支持请求 -- -- 最小部分(仅成果清单)
  2. 过滤器更改 - 区域部分( 过滤器的结果)
  3. 直接导航 -- -- 整页(任务+一切)

部分意见(_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" - 拦截链接,转换为AJAX
  • hx-target="#content" - 向何处注射答复
  • hx-headers='{"pagerequest": "true"}' - 自定义信头告诉控制器它的射速
  • 控制器检查 Request.IsHtmx() && pagerequest 仅返回最小部分

主要是luccid. pagging 助理软件包

我写过 大部分是混杂的。 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; }
}

你会得到什么:

  • 需要零配置
  • 多用户界面框架(TailwindCSCSS + DaisaiUI, Botsstrap 5, 定制视图)
  • 暗模式支持
  • 8种内置语言
  • 可排序的标题, 页面大小选择器
  • 渐进增强( 没有 JavaScript 的工程)
  • 继续给 NOSQL 数据库的象征性支持

标签助手生成链接, 以保存查询字符串, 支持自定义信头, 并天衣无缝地与 HTMX 整合( 见上文示例) 。

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 模板碎片

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 扩展的更多信息, 包括客户端模板, 见 随带的物品,.

挂载和提示( T)

调试工具 : 安装 HTMX 调试器扩展扩展 - 它显示每一个请求,回应, 和交换实时。

CSRF 代号

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反伪造托肯斯的文章 用于完整细节。

阿尔卑斯山(Alpine.js) @Shorthand in Razor

阿尔卑斯山 @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 要截图哪个元素, 在恢复历史时保存周围的布局 。

CCDN Caching (Cloudflare等) (Cloudflare 等)

症状: HTMX在本地工作,但打破了CDN的背后,错误的内容被缓存。

根原因 : CDN 忽略 HX-Request 头页。 您的服务器返回基于此页头的不同内容, 但 CDN 以相同的方式缓存 。

ASP.NET 核心修正: 使用使用 VaryByHeaderNames:

[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" })]

CDN 修正 : 要包含的配置缓存规则 HX-Request 在缓存密钥中。 对于云雾 : Dashboard 自定义缓存密钥 HX-Request.

Hx加速辩论

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 的情况下, 返回到服务器端的简单化 。 您得到 :

  • 动态的、类似于SPA的相互作用
  • 服务器端转换( 用于 SEO )
  • 正确的 HTTP 缓存
  • 最小化 JavaScript
  • 逐步加强
  • HTMX.NET型式安全路线
  • 零配置排程, 多数为lucid.pangtaghelfer 。

服务器改造方法经受了时间的考验,HTMX终于拥有了它应得的优雅的客户端工具。你可以利用已经工作了几十年的模式构建强大的、有表现的网络应用程序 — — 它们只是等待正确的工具来再次发光。

本博客上的相关文章

随带条款

本篇文章是HTMX和ASP.NET Core的两部分系列的一部分:

  1. 本条本条本条 - 侧重于ASP.NET核心整合、部分观点、HTMX.NET和页码
  2. HTMX扩展的 " 口哨 " 巡航 - 深入潜入HTMX事件、生命周期、扩展架构和定制扩展

更多HTMX文章

继续阅读

正式文件:

图书馆和工具 :

社区资源:

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.