我非常激动能够采访@defunkt,他是pjax的作者。pjax是一个早期的面向超媒体的JavaScript库,启发了intercooler.js,后者后来演变为htmx。当然他还做了其他事情,比如联合创立GitHub,但本次访谈我将聚焦于pjax:它是如何诞生的、受何影响以及它又影响了什么。
感谢@defunkt接受采访!
问:首先,请向读者简要介绍您的专业和技术背景:
我想用两个小故事概括我的技术背景:
六年级"展示与讲述"课上,我带了制作的网页打印稿——包括源代码。我想象全班同学都印象深刻。
七年级刚结束,一群吵闹的高中生带我去本地大学的Linux安装大会,在我家旧电脑上安装了Red Hat。这台电脑成了我整个高中的主力机。
所以从一开始,我就是个热爱UNIX的网页开发者。
编程方面,我在祖父母地下室用IBM PC(运行OS/2)的QBasic起步。后来沉迷MUD游戏(以及MUSHes/MUXes/MOOs...),它们用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的人来说,这既自然又合理。
2008年GitHub上线时(惊喜!)由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网页浏览器。
问:能否谈谈pjax的诞生历程?
一切始于XMLHttpRequest(Ajax)。我成长时期网络很简单:点击链接,加载新页面。朴素但美好。
后来人们用
<frames>
等在HTML中构建电邮客户端等应用式程序。不太美好也不太优秀,但有其价值。幸运的是,2000年代中期Gmail和Ajax改变了一切。Hotmail存在已久,但Gmail更快。通过XMLHttpRequest无整页刷新更新内容,可打造媲美桌面应用的体验。虽然Gmail之前也有Ajax应用,但其普及真正让该技术崭露头角。
很快,Ajax与圆角设计开启了Web 2.0时代。到2010年,越来越多开发者将代码移入JavaScript并用Ajax加载动态内容。但有个关键问题:在原始Web模型中,每个页面有唯一URL,可在任意环境加载内容——这是Web的创新。而使用Ajax时URL不变,更糟的是它无法更改(至少服务器无法读取)。Web被破坏了。
传统上,开发者用变通方案绕过限制。#!时代由Facebook/Twitter等重度Ajax网站开创。访问个人主页时,浏览器地址栏显示http://twitter.com/#!/htmx_org而非http://twitter.com/htmx_org。#传统用于锚点链接(定位页面子章节),可被JavaScript修改。这些Web 2.0先驱利用#的可塑性,将其代表可通过内联更新存在的永久内容,类似真实URL。唯一问题是服务器请求中看不到#部分,因此后端架构也需调整。
哦,而且它漏洞百出。
作为HTTP纯粹主义者,我厌恶#!。但当时没有更好方案。
时光流逝,解决方案悄然出现。神奇的一天,#!从Facebook消失,被传统URL取代。难道他们放弃了Web 2.0?不...他们找到了更好方案。
history.pushState()
和history.replaceState()
刚被加入主流浏览器。Facebook迅速利用此API在Ajax更新内容时同步修改浏览器完整URL,让Web重焕荣光。缺失的环节就此补全。
问题解决了,但新问题出现:GitHub不是SPA,我也不希望它是。到2011年我写JavaScript已六年——足够明白过多JS是灾难。2009年左右最初的GitHub问题跟踪器是纯JS开发的Gmail风格应用,对开发者、用户和我都是糟糕体验。
尽管如此,我仍相信Ajax能大幅加速页面UI并提升体验。只是不想为此编写大量(或任何)JavaScript。我喜欢Web构建的简单请求/响应范式。
于是Pjax诞生了。它通过Ajax加载新页面(非整页)加速GitHub UI,正确更新URL且除Pjax库外无需额外JS。开发者只需用
[data-pjax]
标记链接,后端应用自动渲染无布局页面内容,快速获取所需数据而无需浏览器重载未变化的JS/CSS/HTML。它(基本)支持后退键(如常规网页),若需深入"黑暗面"写自定义功能也提供JS API。Pjax首次提交是2011年2月26日,2011年3月底公开——当时我们已用它驱动GitHub.com一段时间。
问:我记得它在Rails社区影响很大。Turbolinks的出现是否影响其采用?
我的目标并非推广这个库。如果是,我可能会花精力解耦它与jQuery。当时我专注于GitHub建设,对既有开源项目维护不足。
我更希望推广这个理念——让人们知道
pushState()
的存在,知道除纯手写JS外还有其他建站方式。服务器端全页/局部渲染依然可行,且能用现代技术加速。Turbolinks诞生并集成进Rails令人惊喜,但也意料之中。GitHub之前我就是Sam Stephenson作品的忠实粉丝,我们对HTTP和Web理念高度一致。我的部分思路受他和Rails社区影响,而吸引我加入Rails社区的正是对Web精髓的共识。
除依赖jQuery外,pjax方案也很局限。它是个简单库。我知道别人能走得更远,而他们确实做到了。
问:pjax有多少"理论"基础?构建时是否深入思考超媒体、REST等?(我是在构建intercooler后才接触理论,好奇您的经历!)
不多。最初给每个请求追加
?pjax=1
,发布前改为发送X-PJAX
头。相当高级。早期GitHub开发者Rick Olson(@technoweenie)也是Rails社区成员,是他向我介绍HATEOAS并在GitHub API中贯彻该理念。pjax任何优点都归功于他和另一位Rails先驱Josh Peek。
我的焦点主要在用户体验、开发者体验以及坚持Web的卓越特性。