我们已经断言一段时间了,许多人采用 SPA 架构构建 Web 应用程序的主要原因是美学考虑。
正如我们在我们的书 Hypermedia Systems 中提到的,当讨论我们开始时使用的 Web 1.0 风格的联系人管理应用程序时,即使它与 SPA 版本具有相同的功能,该应用程序也存在严重的 美学 问题:
从用户体验角度来看:在应用程序的页面之间移动,或者创建、更新或删除联系人时,会注意到明显的刷新。这是因为每个用户交互(链接点击或表单提交)都需要完整的页面刷新,每个操作后都需要处理全新的 HTML 文档。
–Hypermedia Systems - 第 4 章
这种网页之间令人震惊的“ka-chunk”,通常伴随着 Flash of Unstyled Content,一直伴随着我们,虽然现代浏览器在某种程度上改善了这种情况(不幸的是,也使飞行中的请求不那么明显),但情况仍然很糟糕,特别是与精心制作的 SPA 所能实现的效果相比。
在 Web 的早期,这种问题并不是什么大不了的事。我们有星星围绕恐龙 在浏览器的工具栏中 飞舞、闪烁的文本、基于表格的布局、跳舞的婴儿等等,我们主要是在将 Web 与像 ftp 客户端这样的东西进行比较。
标准 很低,时代 美好。
唉,Web 自那以后已经摒弃了那些幼稚的东西,现在我们被期望向用户呈现精致的、吸引人的界面,包括 从一个视图状态到另一个视图状态的平滑过渡。
再次,我们认为这就是为什么许多团队默认采用 SPA 方法:旧方法似乎… 笨拙。
早期的 Web 工程师意识到 Web 开发者希望在不同视图状态之间提供平滑过渡,并提供了各种技术来实现这一点。其中一个主要技术是 CSS Transitions,它允许您指定从一个状态到另一个状态的数学 过渡。
不幸的是,对于 HTML 来说,CSS 过渡只有在使用 JavaScript 时才可用:您必须动态更改元素以触发过渡,而“纯”HTML 无法做到这一点。在实践中,这意味着只有使用 JavaScript 构建 SPA 的“酷孩子”才能使用这些工具,进一步巩固了 SPA 的 美学优势。
htmx,正如您可能知道的,通过某种复杂的 交换模型 使 CSS 过渡 在纯 HTML 中可用,其中我们获取旧内容和新内容中都存在的元素,并“settle”它们上的属性。这是一个巧妙的技巧,可以用于使超媒体驱动的应用程序感觉像精心制作的 SPA 一样丝滑。
然而,有一个新成员加入:视图过渡 API
视图过渡 API 比 CSS 过渡更有雄心,它试图提供一个简单、直观的 API,用于将 整个 DOM 从一个状态过渡到另一个状态,让凡人也能利用它。
此外,这个 API 不仅应该在 JavaScript 中可用,还应该适用于 HTML 中的普通链接和表单,从而使用 Web 1.0 方法构建 更美观 的用户界面成为可能。
当此功能可用时,重访“Hypermedia Systems”中的联系人应用程序将很有趣!
然而,就目前而言,这个 API 像 CSS 过渡一样,仅在 JavaScript 中可用,并且仅在 Chrome 111+ 中刚刚发布。
在 JavaScript 中,这个 API 再简单不过了:
// 这就是从一个状态到另一个状态实现平滑过渡所需的一切!
document.startViewTransition(() => updateTheDOMSomehow(data));
现在,这就是我喜欢的 API。
幸运的是,将这个 API 包装在常规的 htmx 交换模型周围非常简单,这允许我们在 HTML 中普遍可用之前就开始在 htmx 中探索视图过渡!
而且,从 htmx 1.9.0 开始,您可以通过将 transition:true 属性添加到 hx-swap 属性中来开始实验这个 API。
所以让我们来看一个与 htmx 结合使用这个新奇玩具的简单示例。
这样做将涉及两个部分:
我们需要做的第一件事是定义我们想要的视图过渡动画。
:view-transition-old() 和 :view-transition-new() 伪选择器定义名为 slide-it 的视图过渡.sample-transition 类绑定到我们刚刚定义的 slide-it 视图过渡,以便通过该 CSS 类名绑定到元素(视图过渡 API 的更完整细节可以在 Chrome 开发者页面 上找到,该页面记录了它们。)
<style>
@keyframes fade-in {
from { opacity: 0; }
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes slide-from-right {
from { transform: translateX(90px); }
}
@keyframes slide-to-left {
to { transform: translateX(-90px); }
}
/* 为旧内容和新内容定义动画 */
::view-transition-old(slide-it) {
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(slide-it) {
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
/* 将视图过渡绑定到给定的 CSS 类 */
.sample-transition {
view-transition-name: slide-it;
}
</style>
这个 CSS 设置如下:带有 .sample-transition 类的内容在移除时会淡出并向左滑动,而新内容会淡入并从右侧滑动进来。
通过 CSS 定义了我们的视图过渡,接下来要做的是将这个视图过渡绑定到 htmx 将变异的实际元素,并指定 htmx 应利用视图过渡 API:
<div class="sample-transition">
<h1>Initial Content</h1>
<button hx-get="/new-content"
hx-swap="innerHTML transition:true"
hx-target="closest div">
Swap It!
</button>
</div>
这里我们有一个按钮,它发出 GET 请求来获取一些新内容,并用响应替换最近的 div 的 inner HTML。
该 div 带有 sample-transition 类,因此上面定义的视图过渡将应用于它。
最后,hx-swap 属性包含选项 transition:true,这告诉 htmx 在交换时使用内部视图过渡 JavaScript API。
将所有这些联系在一起后,我们就可以开始使用视图过渡 API 与 htmx 一起工作了。这里是一个示例,它应该在 Chrome 111+ 中工作(其他浏览器也能正常工作,但不会获得漂亮的动画):
假设您正在 Chrome 111+ 中查看此页面,您应该会看到上面的内容优雅地向左滑动并被从右侧滑入的新内容替换。很好!
嘿,这很酷,而且一旦您理解了这个概念,并不是什么大工程!这个新 API 显示出很大的潜力。
视图过渡是一种令人兴奋的新技术,我们认为它可以极大地平衡 超媒体驱动应用程序 与当今更流行的 SPA 架构之间的竞争环境。
通过消除 Web 1.0 应用程序丑陋的“ka-chunk”,SPA 方法的美学优势将被削弱,我们可以少关注“sizzle”而更多关注各种架构相关的 实际技术权衡 来做出决策。
我们期待视图过渡在纯 HTML 中可用,但在那之前,您可以从今天开始在 htmx 中玩弄它们!