许多年轻的开发者或许很难想象,在单页应用和 Node.js 尚未席卷一切的那个时代,Web 框架曾经真正“拥有”整个技术栈。数据库的查询抽象、模板的渲染逻辑、用户输入的校验规则、会话的存储机制,乃至最后部署到服务器的目标平台,所有这些组件都在一个内聚的系统里被精心设计,彼此默契配合。开发者走进框架的文档站,就能看清什么是官方推荐的做法,什么又处于地图之外。当你习惯了这种感觉,再回头看如今 JavaScript 生态里的全栈开发,那种割裂感就会格外清晰。Primate 的创始人正是带着这种怀旧与不满,开启了一个新的框架项目,试图重新拿回全栈框架对技术栈的所有权。
Primate 的作者回忆,自己早年几乎是用 PHP 框架构筑了整个 Web 开发观的。那会儿他主要用 Yii,偶尔也接触 Laravel,但真正打动他的其实不是 PHP 这门语言本身,而是框架带来的那种一体感。在那个世界里,数据库访问、模板、路由、校验、MVC 的组织范式,全都装在同一个“盒子”里。你当然可以替换掉其中的一些部件,比如换个模板引擎,或者接入不同的缓存驱动,但所有这些动作依然发生在一个高度协调的系统内部。框架的作者们已经替你考虑好了部件之间的交互边界和约定,你哪怕不太清楚底层的连线,也轻易碰不到那些被称为“接缝”的脆弱地带。这种对全栈的完整所有权,在作者看来,是构建复杂 Web 应用时极为宝贵的设计特质。
当 JavaScript 从浏览器大举进军服务端之后,上述这种“框架拥有整个栈”的理念几乎消失殆尽。Node.js 带来的不仅是事件驱动和非阻塞 I/O,还有一种彻底奉行“组合优于内聚”的工程文化。开发者开始习惯这样组装自己的应用:先挑一个称手的 HTTP 服务框架,再选一个数据库客户端,然后搭配单独的校验库、单独的会话管理库、单独的前端视图层,最后再用打包工具和构建脚本把这一切粘合起来。这种哲学听上去非常 UNIX:每个工具只做好一件事。然而,Web 应用远不止是孤立工具的线性管道,它天然包含着大量需要共享的上下文。请求体的结构规范、校验的边界规则、会话的管理策略、渲染的入口约定、路由与序列化格式的映射、对应不同部署环境的适配逻辑——所有这些元信息本应在一个体系内被统一而自然地表达。现实却是,问题常常就出在这些工具们碰面的“接缝”处,而且往往以极细微的方式暴露出来,让人排查到深夜。
元框架(Meta-frameworks)的出现让情形有所好转,但这种改善方式,更像是在五花八门的身体上强行套入同一件紧身衣。Next.js 给出了一个高度一致的技术栈,但它本质上是一个 React 的技术栈。如果某天你发现了 Solid 的响应式优势,想在自己的应用里尝试一下,对不起,这不是在视图层换个库那么简单。你没法把 React 的视图渲染轻轻拨到一边,然后把 Solid 的组件树接上去。你要做的,是整体迁移到另一个为 Solid 打造的元框架里去,而这个新框架会带着一套不同的约定、不同的路由细节、不同的后端集成假设,甚至不同的数据获取模式。同样的事情在生态里反复上演:React 有一个元框架,Solid 有另一个,Svelte 有自己的,Vue 也有自己的。每个框架都在重新解决几乎一样的后端问题,但解决方式又都带着点微妙的差异,逼着开发者每当想换个前端方案时,就必须重学一遍那些本不该成为负担的东西。
运行时(Runtime)的碎片化让局面雪上加霜。Node.js、Deno、Bun 都是具备生产力水准的 JavaScript 运行时,但如今一些框架已经实际上与特定的运行时绑定在了一起。这意味着你的应用不只是被锁在一个前端库里,还会被进一步锁死在一个运行时上。即便某些框架宣称自己“支持”Deno 或 Bun,这个“支持”的含义经常不过是该运行时刚好兼容 Node.js 的 API,能够把为 Node 写的代码跑起来而已。这离“框架在设计上就充分利用了各个运行时的原生 API 和执行模型”还差得很远。于是,当你试图用 Deno 原生的 fetch 实现去替代某个 Node 特有的网络库时,往往会在框架约定和运行时的实际行为之间撞上一堵隐形的墙。
在 Primate 的作者看来,这种种绑定毫无必要。前端的本质就是一套描述浏览器用户界面的方案,后端的本质是处理请求、数据和业务逻辑的模型,而运行时的本质则是代码的执行目标平台。这三者不应该被强制焊死在一个固定的组合里,它们天然就应该被允许独立演化、自由搭配。你完全应当能够用 React 写出一个路由,用 Svelte 写另一个,再用 Marko 写完第三个,而整个后端模型、校验逻辑和会话管理都不需要为此做出任何实质性改变。同理,写后端路由的方式也不该被某个特定的元框架所垄断,更不应随着你选择的前端库不同而被迫重写。
这种灵活性不是那种“理论上可以”的虚空许诺。Primate 的核心设计目标,恰恰就是把前端视图层、后端业务层和底层运行时的选择权,真正交还给开发者。它并不试图成为又一个带着自己强烈偏好的元框架,而是要扮演一种更高维度的粘合角色:在保留各组件独立性与最佳实践的前提下,提供一个让它们能够安然共处的温层。当框架真正拥有全栈所有权,它提供的不是僵硬的捆绑,而是一套清晰规划好的契约——让你确切地知道,在哪些范围内是你熟悉的领土,在哪些边界之外你需要格外留神。这样,你才不会在每个框架的文档地狱里反复迷失,也才能在技术潮流更迭时,不被整套技术栈的迁徙成本拖垮。
回看历史,PHP 全栈框架的遗产与其说是某个具体的技术方案,不如说是一种工程哲学:一个好的 Web 框架应当为你把各层之间的关联都料理妥当,让你可以专注于业务本身,而不是在粘合工具的泥潭里消耗心力。今天层不出穷的前端方案和运行时变局,反而让这种哲学有了更现实的回归理由。Primate 的出现,或许正提醒着社区:与其在每种前端技术后面都跟一套复杂的全栈家族,不如反过来思考,怎样让同一个坚实的后端世界,从容地挂载起多种多样、并且可以随时替换的视图层。这种愿景听起来过于理想化,但至少,已经有人开始认真地把它变成一行行能够运行的代码。而这,本身就是一个值得兴奋的开始。
热门跟贴