与 Chris Wanstrath(又名 @defunkt),pjax 的创建者的一次访谈

Carson Gross

我非常兴奋能够采访 @defunkt,他是 pjax 的作者,这是一个早期的面向超媒体的 JavaScript 库,它为 intercooler.js(后来发展为 htmx)提供了灵感。他还做过其他一些事情,比如联合创办 GitHub,但在这篇访谈中,我想重点关注 pjax,它是如何产生的、什么影响了它,以及它又影响了什么。

感谢你同意接受访谈,@defunkt!

Q: 首先,你能向读者简要介绍一下你的专业和技术背景吗:

我想用两个简短的轶事来总结我的大部分技术背景:

  1. 在六年级“展示与讲述”活动中,我带来了一份我制作的网页打印件——包括其源代码。我喜欢想象每个人都印象深刻。

  2. 七年级刚结束,一群喧闹的高中生带我去当地大学参加一个 Linux 安装节,并在我们家旧 PC 上安装了 Red Hat。那成为我整个高中时期的主要电脑。

所以从一开始,我就基本上是一个热爱 Web 和 UNIX 的嬉皮士。

在编码方面,我是从在祖父母地下室的 IBM PC 上运行 OS/2 开始使用 QBasic 的。然后我深入研究了 MUD(以及 MUSH、MUX 和 MOO……),这些是用 C 语言编写的,通常包含自己的自定义脚本语言。用 C 编写是“硬编码”,编写脚本是“软编码”。我在 C 中不知道自己在做什么,但非常喜欢软编码的部分。

那群介绍我认识 Linux 的喧闹高中生给了我 O’Reilly 的骆驼书,并告诉我去学习 Perl。我并不享受它。但他们还向我展示了 php3,突然一切都连起来了:HTML 与类似 MUD 的软编码相结合。我上瘾了。

我尝试过其他东西,如 ASP 3.0 和 Visual Basic,但最终 PHP 成为我整个高中时期的首选。我热爱制作动态网页,也热爱 Linux 服务器。我和朋友们在高中时有一个喜剧网站(名称保密),我在博客软件流行之前自己编写了整个 mysql/php 后端。那太有趣了。

大学第一年,我切换到 Gentoo,并对他们的包管理器着迷,它是用 Python 编写的。你可以用它编写真正的 Linux 工具,这很了不起,但当时 Web 方面的故事感觉很薄弱。

我买了那本厚厚的 Python O’Reilly 书,正在逐步学习时,随机地发现了 Ruby on Rails。它像一道闪电击中了我,突然我的 PHP 和 Python 时代结束了。

与此同时,Web 2.0 刚刚被命名,JavaScript 就像在说:“嘿,大家。我一直都在这里。” 所以在学习 Rails 的同时,我也学习了 JavaScript。Rails 有助手来抽象 JS,但我其实非常喜欢这个语言(大部分时间),并想在不依赖框架或库的情况下学习它。

管理自己的 Linux 服务器、在 Rails 中编写后端代码、在 JavaScript 中编写前端代码的组合,让我更深地爱上了 Web 作为平台的魅力,并让我接触到 REST 和 HATEOAS 等概念。作为一个已经编写 HTML 超过十年的开发者,这些感觉很自然且合理。

GitHub 于 2008 年推出,由——惊喜——Gentoo、Rails 和 JavaScript 驱动。但由于 GitHub 不仅仅是一个 Rails 社区,而是编程社区的集合,我很快演变为一个大规模的多语言开发者。

我回去学习了 Python,参加了一些编程竞赛如 Django Dash,并出席(并演讲)不同的 PyCon。我学习了 Objective-C 并制作了 Mac(后来是 iPhone)应用。我学习了 Scheme 和 Lisp,最终从 Vim 切换到 Emacs 并编写了大量 Emacs Lisp。我回去学习了 Perl 中所有符号的含义。然后是 Lua、Java、C++、C,甚至 C#——我想尝试一切。

直到今天我还是这样。我用 Go、Rust、Haskell、OCaml、F#、各种 Lisp(Chicken Scheme、Clojure、Racket、Gambit)等编写了项目。我编写了十多种编程语言,包括几个能实际做事的语言。现在我正在学习 Zig。

但我总是回到 Web。这是为什么我使用 Web 技术创建了 Atom 文本编辑器,为什么 Electron 存在,也是为什么我和 Andreas Kling 刚刚联合创办了 Ladybird 浏览器倡议,来开发独立的开源 Ladybird Web 浏览器。

Q: 你能告诉我 pjax 的起源历史吗?

一切都从 XMLHttpRequest 开始,当然是 Ajax。在我成长的过程中,上学时要来回走路,还要在雪中上坡,Web 很简单:你点击一个链接,新网页加载。没什么花哨的。那是美好的事物,而且很好。

然后人们开始用 <frames> 和相关技术在 HTML 中构建电子邮件客户端和各种类似应用程序的东西。它并不很美观,也不很好,但其中有一些东西。

幸运的是,在 2000 年代中期,Gmail 和 Ajax 改变了这一切。Hotmail 已经存在一段时间了,但 Gmail 很快。通过使用 XMLHttpRequest 在不进行完整页面加载的情况下更新内容,你可以制作一个感觉像桌面应用程序的网页,而无需诉诸框架或其他把戏。虽然其他网站在 Gmail 之前就使用了 Ajax,但 Gmail 变得如此流行,以至于真正将这项技术推向了舞台中央。

很快,Ajax 连同为网页添加圆角的能力,开启了被称为 Web 2.0 的时代。到 2010 年,越来越多的 Web 开发者将越来越多的代码推入 JavaScript,并使用 Ajax 加载动态内容。只有一个问题:在原始的、良好的 Web 模型中,每个页面都有一个唯一的 URL,你可以用它在任何上下文中加载其内容。这是 Web 的创新之一。然而,使用 Ajax 时,URL 不会改变。更糟糕的是,它无法改变——至少服务器读取的部分无法改变。Web 被破坏了。

按照传统,开发者创建了 hack 来绕过这一限制。#! 时代开始了,由像 Facebook 和 Twitter 这样 Ajax 密集型网站率先使用。不是 http://twitter.com/htmx_org,你会在浏览器 URL 栏中看到 http://twitter.com/#!/htmx_org 当访问某人的个人资料时。# 传统上用于锚点标签,链接到完整网页内的子部分,并且可以由 JavaScript 修改。这些古老的 Web 2.0 开发者利用了 # 的可塑性,并开始用它来表示可以内联更新的永久内容,很像真正的 URL。唯一的问题是,当服务器处理请求时,你的服务器代码永远看不到 URL 的 # 部分,所以现在你需要开始改变你的后端架构来让一切正常工作。

哦,而且一切都非常 buggy。那也是一个问题。

作为 HTTP 纯粹主义者,我厌恶 #!。但我没有更好的方法。

时间流逝,看啊,一个解决方案出现了。在一个神奇的日子,#! 从 Facebook 中悄然消失,被老式的 URL 取代。他们放弃了 Web 2.0 吗?不……他们找到了更好的方法。

history.pushState() 函数,连同它的兄弟 history.replaceState(),最近被添加到所有主要 Web 浏览器中。Facebook 迅速利用这个新 API,在通过 Ajax 更改内容时更新浏览器中的完整 URL,将 Web 恢复到之前的荣耀。

于是它出现了:缺失的环节。

我们有了解决方案,但现在一个新问题:GitHub 不是 SPA,我也不想让它成为一个。到 2011 年,我已经编写 JavaScript 六年——足够的时间知道太多 JS 是可怕的事。原始的 GitHub Issue Tracker 是一个 Gmail 风格的 Web 应用程序,完全用 JS 构建,大约在 2009 年。那对我、GitHub 开发者以及最终的用户来说都是可怕的体验。

尽管如此,我仍然相信 Ajax 可以显著加速网页的用户界面并改善整体体验。我只是不想通过编写大量(或任何)JavaScript 来实现它。我喜欢 Web 基础上的简单请求/响应范式。

于是,Pjax 诞生了。它通过 Ajax 加载新页面而不是完整页面加载来加速 GitHub 的 UI,正确更新 URL,而无需除了 Pjax 库本身之外的任何 JS。我们的开发者只需用 [data-pjax] 标记一个链接,我们的后端应用程序就会自动渲染页面的内容而不带布局,快速获取你需要的数据,而无需让浏览器重新加载任何不需要更改的 JS 或 CSS 或 HTML。它也(大部分)与后退按钮兼容,就像普通网页一样,如果你需要深入黑暗面编写自定义东西,它还有一个 JS API。

Pjax 的第一个提交是 2011 年 2 月 26 日,它在 2011 年 3 月下旬公开发布,在我们已经使用它来驱动 GitHub.com 一段时间之后。

Q: 我记得它在 Rails 社区中引起了很大轰动。Turbolinks 的出现是否影响了它在那里的采用?

我的目标并不是库的采用。如果是,我可能会投入工作来将它与 jQuery 解耦。当时,我正深入构建 GitHub,并不是我众多现有开源项目的最佳管理者。

我想要的反而是这个想法的采用——我想让人们知道 pushState(),我想让人们知道有方法构建网站,而不仅仅是手动用 JavaScript 做一切。在服务器上整体或部分渲染页面仍然是可行的,并且可以使用现代技术加速。

看到 Turbolinks 被创建并集成到 Rails 中真是太棒了,并非完全出人意料。我甚至在 GitHub 之前就是 Sam Stephenson 工作的忠实粉丝,我们对 HTTP 和 Web 的想法非常相似。我的部分思考受到了他和 Rails 社区的影响,而吸引我加入 Rails 社区的部分原因是围绕 Web 伟大之处的共享想法。

除了与 jQuery 耦合之外,pjax 的方法相当有限。它是一个简单的库。我知道其他人可以进一步发展它,我很高兴他们这样做了。

Q: pjax 中有多少“理论”?你在构建它时是否考虑了很多关于超媒体、REST 等的内容?(我在构建 intercooler 之后才倒推理论,好奇你的情况如何!)

不多。它开始时是通过将 ?pjax=1 附加到每个请求,但发布前我们切换为发送 X-PJAX 头。非常花哨。

早期的 GitHub 开发者 Rick Olson(@technoweenie),也来自 Rails 社区,是介绍我认识 HATEOAS 并在 GitHub 的 API 中推动这一哲学的人。所以 Pjax 的任何好东西都来自他和 Josh Peek,另一个早期的 Rails 开发者。

我的重点主要是用户体验、开发者体验,以及试图坚持 Web 伟大的东西。

</>