通过 WebSockets 传输 HTML:几乎无需 JavaScript 的实时 SPA

通过 WebSockets 发送服务器渲染的 HTML,以极少的 JavaScript 构建实时单页应用,正在 web 开发者之间引发争论。支持者认为,这种方式简化了状态管理,避免了复杂的客户端 API,并且对许多 CRUD 和内部工具来说,配合 LiveView、Hotwire、htmx 或 Datastar 等框架时,性能可能优于传统 SPA。批评者则反驳说,HTTP/2+SSE 往往能以更少的运维风险达到相近的延迟,服务器持有的会话状态会使扩展性和缓存更复杂,而且业界多次因为充分的理由转向不那么紧耦合的服务端渲染。

关于通过 WebSockets 传输 HTML 的整体反应

  • 许多人认为这只是“MPA 加了些额外步骤”,或者是在重新发明旧模式(DHTML、WebForms、Meteor、ASP.NET Ajax、JSF Ajax)。
  • 支持者认为它能以“10% 的代码实现 90% 的 SPA 优势”:实时更新、无需单独的 JSON API,以及让服务器成为唯一事实来源。
  • 批评者担心它与普通的服务端渲染 HTML 重复,增加基础设施和故障模式,而且企业防火墙有时会阻止 WebSockets。

对比:WebSockets vs SSE vs HTTP

  • 有几位认为,对大多数应用来说,SSE + fetch() 更简单、更易运维,而且对于“仅推送”的实时更新已经足够好。
  • 其他人反驳说,延迟并不等价,尤其是在 HTTP/1 上;而 WebSockets 提供有序消息保障和双向协议,对聊天、游戏和复杂事件流很有用。
  • 指出 SSE 的限制:仅支持文本、没有有序性保证、在 HTTP/1 下每个来源有连接数限制。
  • 讨论还涉及 HTTP/2/3 的多路复用和 QUIC,一些人提到 WebTransport 作为更新、更低延迟的选择。

现有框架与前人工作

  • 多个框架被提到已经在做“通过网络传输 HTML”:Rails Turbo/Hotwire、Phoenix LiveView、Blazor Server、HTMX、Datastar、Laravel Livewire、Symfony Live Components、Inertia.js。
  • 对 Turbo frames/streams 的详细解释展示了如何以极少的 JS 实现局部更新和由服务器发起的广播。
  • 有人认为 Datastar/HTMX 风格的 SSE + DOM morphing 已经在不使用 WebSockets 的情况下提供了类似的收益。

架构、状态与 REST

  • 围绕这种方法是否“RESTless”展开了争论:每连接的服务器状态与 REST 的无状态性相冲突,但对许多内部工具来说,它能简化编程。
  • 支持者喜欢所有权威状态都保留在服务器端,从而避免客户端/服务器状态不同步,并且相比 SPA 减少一半代码量。
  • 怀疑者强调长连接带来的扩展性、负载均衡、DoS 风险和监控复杂性。

开发者体验与 UX 权衡

  • 许多人欣赏避免大型 JavaScript 包和构建流水线;而另一些人更喜欢把 SPA 的数据即事实来源模型(例如带类型 schema 的 Vue/React)作为首选。
  • 对 DOM 替换副作用的抱怨很多(焦点丢失、滚动跳动);idiomorph/Datastar 之类的库试图缓解这些问题,但会增加额外的胶水代码。
  • 总体感觉是,这种模型最适合管理后台、内部工具和交互适度的应用,而不太适合高度响应式的消费级 UI。

元话题:AI 署名争议

  • 同一“系列”中的后续文章被多位评论者指认为 AI 生成;作者对此进行了反驳。
  • 这引发了关于生成式 AI 披露规范、读者不信任以及技术写作真实性感知影响的旁支讨论。