← 返回信息流
AI 资讯Hacker News·2 小时前

Primate Is the Last Great Web Framework

AI 深度解读

背景

Web 框架的演进经历了从 PHP 时代到 JavaScript 时代的巨大转变。Primate 的作者回顾了自己使用 PHP 框架(主要是 Yii 和 Laravel)构建 Web 应用的经历:这些框架拥有完整的栈掌控权——路由、渲染、数据、验证、会话、部署目标、运行时支持,所有组件都经过设计,能够无缝地协同工作。开发者可以进入框架的官方文档,清楚地知道哪些是官方支持的领域,哪些超出了框架的地图。

然而,当 JavaScript 进入服务器端之后,这种“全栈拥有”的理念几乎消失了。JavaScript 生态强烈推崇组合(composition)而非内聚(cohesion)。开发者需要自行选择服务器框架、数据库客户端、验证库、会话管理、前端框架、构建工具,然后手动将它们拼合在一起。每个独立组件可能都做得很出色,但没有人负责组件之间的接缝(seams)。Web 应用并不是孤立工具的管道,它们充满了共享的假设:请求形状、验证边界、会话处理、渲染、路由、序列化、部署目标。在实际开发中,问题往往就发生在这些工具的交界处。

元框架(Meta‑framework)虽然改善了这种情况,但只是通过强行绑定来解决问题。例如 Next.js 提供了一个内聚的栈,但它是一个 React 栈。如果开发者发现了 Solid 并想尝试,不能简单地切换视图层,而必须迁移到另一个拥有不同约定、不同路由细节、不同后端假设的元框架。这种模式在整个生态中反复出现:文件系统路由 + 一个受祝福的前端,被一次又一次地重造。React 有一个元框架,Solid 有另一个,Svelte 有另一个,Vue 有另一个。每个元框架解决的大多是同样的后端问题,但实现方式略有不同,开发者每次都需要重新学习。

运行时碎片化使情况变得更糟。Node、Deno 和 Bun 都是能力足够的 JavaScript 运行时,但现在许多框架实际上变成了运行时特定。这意味着应用不仅可能被绑定到一个前端,还可能被绑定到一个运行时。当一个框架声称“支持”Deno 或 Bun 时,通常只是指该运行时足够兼容以运行 Node 代码,并不代表框架被设计成使用每个运行时的自有 API 和执行模型。

作者认为,这些绑定完全没有必要。前端只是描述浏览器 UI 的方式,后端只是处理请求、数据和应用逻辑的方式,运行时只是执行目标。这三者应该允许独立变化。

核心内容

Primate 正是为了解决上述问题而创建的 Web 框架。它的核心理念是:一个 Web 框架应该拥有整个栈,但不拥有你的选择(own the stack without owning your choices)。具体来说,Primate 提供了统一的应用模型,但允许你在路由级别(route by route)自由选择:

  • 前端:可以在同一个应用中为不同的路由分别使用 React、Svelte、Marko 等不同的前端框架,而保持相同的后端模型。例如,一个路由用 React 渲染,另一个路由用 Svelte 渲染,它们共享相同的路由、验证、会话处理逻辑。
  • 后端语言:路由的后端处理函数可以用 TypeScript、Go、Ruby 或 Python 编写,并组合在同一个应用里。
  • 运行时:同一个应用可以在 Node、Deno 或 Bun 上运行,无需修改一行代码,也不需要使用运行时特定的桥接包。

同时,Primate 仍然为验证、数据库存储、会话、国际化、路由和渲染提供官方支持。它不是被炒作的新玩具,也不是为了在基准测试中刷分;它的目的不是追求新颖,而是消除耦合、锁定以及由他人代做的决策。当你在官方栈内工作时,接缝的责任由 Primate 承担。

作者用一个极简的例子展示了这一点(以下为原文中的关键代码简化描述):

  1. 创建一个项目,安装 primate 及其针对 React 和 Svelte 的模块。
  2. 在配置文件中注册 @primate/react@primate/svelte 模块。
  3. 定义路由 /,返回一个包含标题和链接列表的页面,并分别创建 .tsx.svelte 文件作为同路由的 React 页面和 Svelte 页面——实际上这里 / 路由只用了 React,但后续 /react/svelte 路由分别使用不同的前端。
  4. routes/react.ts 中定义后端逻辑,使用 response.page() 返回数据;然后创建 routes/react.tsx 用 React 渲染该页面。
  5. routes/svelte.ts 中定义相同的后端逻辑,然后创建 routes/svelte.svelte 用 Svelte 渲染。
  6. 使用 npx primate 运行(或用 Deno 的 deno run -A npm:primate,或用 Bun 的 bunx --bun primate)。

关键点:后端路由模型没有变化。应用并没有变成 React 应用或 Svelte 应用,它始终是一个 Primate 应用,每个路由自行选择最适合它的前端。不过作者也承认存在限制:不同前端框架目前无法共享布局,因为布局本身由单一前端渲染;未来可能通过 Web Components 实现。

关键要点

  • 全栈所有权与选择自由:Primate 声称自己是最后一个伟大的 Web 框架,因为它同时做到了「拥有完整的栈」和「不限制开发者的选择」。它不像传统全栈框架那样强制绑定特定的前端、后端语言或运行时。
  • 路由级前端选择:同一个 Primate 应用中,不同的路由可以使用不同的前端框架(React、Svelte、Marko 等),而共享相同的后端模型(路由定义、验证、会话等)。
  • 多后端语言支持:除了 TypeScript,后端路由也可以用 Go、Ruby 或 Python 编写,并组合在同一个应用内。
  • 运行时无关:应用代码无需修改即可在 Node、Deno 或 Bun 上运行,无需依赖运行时特定的包。
  • 官方支持的基础设施:Primate 仍然提供验证、数据库存储、会话、国际化、路由、渲染等官方模块,确保在官方栈内工作时无接缝问题。
  • 反对碎片化与锁定:作者批评当前 JavaScript 生态中,每一个前端框架都催生一个元框架,每一个运行时都催生运行时专用框架,导致开发者需要为每一对组合重新学习且难以迁移。
  • 当前限制:不同前端框架无法共享布局(布局由单一前端渲染)。未来可能通过 Web Components 解决。
  • 非炒作,非基准竞赛:Primate 的意义不在于时髦或性能数字,而在于解除耦合、消除供应商锁定,让框架负责接缝,开发者负责选择。

意义与影响

Primate 的提出挑战了当前 JavaScript 生态中根深蒂固的范式:即一个 Web 框架必须绑定一个前端框架、一个后端技术栈和一个运行时。它试图回归到 PHP 时代那种“一个框架拥有全部,但又不强制一切”的理想状态,同时拥抱现代 JavaScript 的多前端、多语言、多运行时环境。

如果 Primate 能够获得广泛采用,它可能带来以下影响:

  1. 降低技术迁移成本:开发者可以在不改变整个应用架构的情况下,逐步替换前端框架、后端语言或运行时。例如,在新路由中尝试 Svelte,而保留原有的 React 路由;或者将应用从 Node 迁移到 Bun 而无需修改业务代码。
  2. 促进前端框架的平等协作:不再需要为每个前端框架构建单独的元框架。Primate 提供了一个通用的后端抽象,任何前端框架都可以通过适配器接入,从而让不同框架在同一应用中和平共处。
  3. 减少生态碎片:当前,每一个前端框架、每一个运行时都催生了一个元框架生态,开发者面临很多“几乎一样但细节不同”的选择。Primate 试图统一后端和运行时接口,使这些选择变得正交。
  4. 强调“接缝”的责任:在传统的组合式生态中,开发者自己负责组件间的接缝,容易出错。Primate 将接缝视为框架的责任,使开发者可以更专注于业务逻辑。
  5. 对现有元框架的挑战:Next.js、Nuxt、SvelteKit 等框架是目前的主流选择,但它们都与特定前端深度绑定。Primate 提供了一种非绑定的替代方案,可能会吸引那些希望保持灵活性的团队。

当然,Primate 仍是一个较新的项目,其生态系统成熟度、社区

查看原文 →superarch.org