Stimulus 的起源
我们在 Basecamp 编写了很多 JavaScript,但我们不会用它来创建当代意义上的“JavaScript 应用程序”。我们所有的应用程序的核心都是服务器端呈现的 HTML,然后添加一些 JavaScript 来让它们闪耀。
这是 雄伟的单体 的方式。Basecamp 运行在六个平台上,包括原生移动应用程序,使用 Ruby on Rails 创建了一组控制器、视图和模型。拥有一个可以在一个地方更新的单一共享界面对于能够在小型团队中执行任务至关重要,尽管有许多平台。
它允许我们像过去一样享受生产力。回到一个程序员可以在不陷入间接层或分布式系统的情况下取得巨大进步的时候。在每个人都认为圣杯是将他们的服务器端应用程序限制为为基于 JavaScript 的客户端应用程序生成 JSON 之前。
这并不是说这种方法对某些人来说没有价值,只是有时。只是作为许多应用程序的一般方法,当然像 Basecamp 这样的应用程序,它在整体简单性和生产力方面是一种倒退。
这也不是说单页面 JavaScript 应用程序的激增没有带来真正的收益。其中最主要的是更快速、更流畅的界面,摆脱了全页面刷新。
我们也希望 Basecamp 感觉像那样。就好像我们已经追随了这个群体,用客户端渲染重新编写了所有内容,或者在移动端完全原生化。
这种愿望使我们找到了一个双管齐下的解决方案:Turbo 和 Stimulus。
﹟ Turbo 高处,Stimulus 低处
在我介绍 Stimulus,我们新的适度 JavaScript 框架之前,请允许我回顾 Turbo 的主张。
Turbo 源自 GitHub 开发的一种名为 pjax 的方法。基本概念保持不变。整页刷新通常感觉很慢的原因并不是因为浏览器必须处理从服务器发送的一堆 HTML。浏览器在这方面非常出色且速度非常快。而且在大多数情况下,HTML 有效负载往往大于 JSON 有效负载的事实也无关紧要(尤其是使用 gzip)。不,原因是必须重新初始化 CSS 和 JavaScript 并重新应用于页面。无论文件本身是否已缓存。如果您有相当数量的 CSS 和 JavaScript,这可能会非常慢。
为了解决此重新初始化问题,Turbo 维护了一个持久进程,就像单页应用程序所做的那样。但基本上是一个不可见的进程。它拦截链接并通过 Ajax 加载新页面。服务器仍然返回完全成型的 HTML 文档。
仅此策略就能让大多数应用程序中的大多数操作感觉非常快(如果它们能够在 100-200 毫秒内返回服务器响应,这通过缓存完全有可能)。对于 Basecamp,它将页面到页面的转换速度提高了约 3 倍。它赋予应用程序响应性和流畅性,这是单页应用程序吸引力的重要组成部分。
但 Turbo 只是故事的一半。粗略的一半。在完整页面更改的级别下方,是单个页面内的所有精细保真度。显示和隐藏元素、将内容复制到剪贴板、向列表中添加新待办事项以及我们与现代 Web 应用程序关联的所有其他交互的行为。
在 Stimulus 之前,Basecamp 使用不同的样式和模式来应用这些点缀。一些代码只是少量的 jQuery,一些代码是同样少量的不含任何框架的 JavaScript,还有一些是较大的面向对象子系统。它们通常都使用挂在 data-behavior
属性上的显式事件处理。
虽然像这样添加新代码很容易,但这不是一个全面的解决方案,并且我们有太多内部样式和模式共存。这使得代码难以重用,并且使得新开发人员难以学习一致的方法。
﹟ Stimulus 中的三个核心概念
Stimulus 将这些模式中最好的部分汇总到一个适度的小型框架中,该框架仅围绕三个主要概念展开:控制器、操作和目标。
当您查看它所解决的 HTML 时,它被设计为渐进式增强。这样,您就可以查看单个模板并了解哪种行为正在对其执行操作。这是一个示例
<div data-controller="clipboard">
PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
<button data-action="clipboard#copy">Copy to Clipboard</button>
</div>
你可以阅读它,并对正在发生的事情有一个很好的了解。即使不知道任何有关 Stimulus 的信息或没有查看控制器代码本身。它几乎就像伪代码。这与阅读一段 HTML 代码非常不同,该 HTML 代码有一个外部 JavaScript 文件向其应用事件处理程序。它还保持了在许多当代 JavaScript 框架中丢失的关注点分离。
如你所见,Stimulus 不会费心创建 HTML。相反,它会附加到现有的 HTML 文档。在大多数情况下,HTML 会在页面加载时(首次点击或通过 Turbo)或通过更改 DOM 的 Ajax 请求在服务器上呈现。
Stimulus 关注于操作此现有 HTML 文档。有时这意味着添加一个隐藏元素、对其进行动画处理或突出显示它的 CSS 类。有时这意味着按分组重新排列元素。有时这意味着操作元素的内容,例如当我们将可缓存的 UTC 时间转换为可显示的本地时间时。
在某些情况下,你希望 Stimulus 创建新的 DOM 元素,并且你绝对可以自由地这样做。我们甚至可能添加一些糖,以便将来更容易。但这是少数派用例。重点在于操作元素,而不是创建元素。
﹟ Stimulus 与主流 JavaScript 框架有何不同
这使得 Stimulus 与大多数当代 JavaScript 框架非常不同。几乎所有框架都专注于通过某种模板语言将 JSON 转换为 DOM 元素。许多框架使用这些框架生成一个空页面,然后该页面仅填充通过此 JSON 到模板渲染创建的元素。
Stimulus 在状态问题上也有所不同。大多数框架都有在 JavaScript 对象中维护状态的方法,然后根据该状态呈现 HTML。Stimulus 恰恰相反。状态存储在 HTML 中,以便可以在页面更改之间丢弃控制器,但在缓存的 HTML 再次出现时仍按原样重新初始化。
这确实是一个非常不同的范例。我相信许多习惯于使用当代框架的资深 JavaScript 开发人员会对此嗤之以鼻。嘿,尽情嘲笑吧。如果你对在 React + Redux 等混乱中维护应用程序所需的复杂性和工作量感到满意,那么 Turbo + Stimulus 不会吸引你。
另一方面,如果你有一种挥之不去的感觉,即你正在处理的事情不值得如此复杂的复杂性和应用程序分离,那么你很可能会在我们的方法中找到庇护。
﹟ Stimulus 及相关思想是从野外提取的
在 Basecamp,我们多年来一直在多个不同版本的 Basecamp 和其他应用程序中使用此架构。GitHub 也使用类似的方法取得了很好的效果。这不仅是主流对“现代”Web 应用程序外观理解的有效替代方案,而且还是一个令人难以置信的引人注目的替代方案。
事实上,这感觉就像我们在开发Ruby on Rails时在 Basecamp 拥有的那种秘方。当代主流方法不必要地复杂,而且我们能够以更少的方式做得更多、更快。
此外,您甚至不必选择。Stimulus 和 Turbo 与其他更重量级的方法结合使用时效果很好。如果您的应用程序的 80% 不需要大型设备,请考虑为此使用我们的双重组合。然后为真正能从中受益的应用程序部分推出重型机械。
在 Basecamp,当需要时,我们已经并确实使用了多种更重量级的方法。我们的日历倾向于使用客户端渲染。我们的文本编辑器是 Trix,这是一个完全成型的文本处理器,不适合作为一组 Stimulus 控制器。
这组备用框架旨在尽可能避免繁重的工作。为了在所有与该简单模型配合良好的许多交互中保持在请求-响应范例中。然后在需要峰值保真度时使用昂贵的工具。
最重要的是,它是一个工具包,适用于希望在保真度上竞争并使用更费力、更主流的方法与更大的团队竞争的小团队。
尝试一下。
David Heinemeier Hansson