前端作为客户端 REST 服务器:架构纪律取代状态管理混乱
现代前端开发早已超越了 DOM 操作和简单布局的阶段。从架构角度看,SPA 和 SSR 应用执行的任务与经典服务器系统完全相同:接收输入信号,按照业务逻辑规则处理数据,并返回就绪的视图。区别仅在于执行环境和交互协议。与其为每个框架版本无休止地发明新抽象,不如直接将经过验证的服务器模式应用到客户端代码中。
角色演变:从超媒体到客户端渲染
最初,Web 运作模式非常透明:浏览器请求一个 URL,服务器生成 HTML,客户端显示结果。令人惊讶的是,标准的 HTML 就是 HATEOAS 原则的原生实现。服务器返回的不仅仅是数据,还有接口状态以及可用的转换:链接、带有指定方法的表单、操作按钮。客户端无需提前存储路由图——它只需响应当前上下文允许的操作。
现代使用 JSON API 的方式仅仅改变了状态传输格式,但本质未变。相同原则也可以通过 HAL 格式表达:
{
"id": 42,
"status": "pending",
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42/cancel", "method": "POST" },
"detail": { "href": "/orders/42/detail" }
}
}
UI = f(state) 这个公式早在虚拟 DOM 出现之前就已存在。以前,状态完全从远程服务器传输;如今,它在本地计算并增量更新。问题并非源于范式转变,而是将逻辑移植到客户端时失去了架构纪律。
事件即请求:映射架构层
在后端开发中,交互受到严格规范。控制器接收 HTTP 请求,验证输入数据,将其转换为 DTO,并委托服务层处理。服务层操作领域模型,而仓库层负责持久化。职责边界清晰,与具体语言或框架无关。
在前段,用户操作(点击、输入、滚动、WebSocket 消息)完全等同于 HTTP 请求。事件 = 请求。然而,前端生态并未采用统一的控制器,而是衍生出一堆术语:stores、composables、hooks、atoms、runes、view models。每种工具都强加自己的状态管理理念,模糊了架构边界。更合理的做法是将客户端架构与服务器标准对齐:
Frontend
웃 → Event → Controller → Service → Repository → → → → → → Rest API
│ │ │ ↑ │ │
│ │ │ │ └─ Builds HTTP Request │
│ │ │ │ │
│ │ │ └─ Business logic │
│ │ │ │
│ │ └─ Extracts, validates & maps to DTO │
│ │ │
│ └─ Environment detail - Browser Event │
│ │
│ │
│ │
└─ ← Response ← Controller ← Service ← Repository ← ← ← ← ← ← Rest API
│ │ │
│ │ └─ Maps to Domain Model
│ │
│ └─ Maps to framework format: JSX/Vue template
│
└─ Automatically mapped to HTML chunk from JSX by framework
有状态控制器与基础设施细节
直接移植服务器模式的主要异议涉及状态。经典 REST 控制器设计为无状态,以支持水平扩展。前端本质上运行在有状态上下文中:用户会话、本地缓存、表单和 UI 元素状态驻留在浏览器内存中。这并非矛盾,而是环境特性。在后端,类似的有状态模式常用于游戏开发、IoT 网关以及基于 gRPC 或 WebSockets 的流媒体服务。
Hooks、signals 和响应式 atoms 应仅视为基础设施细节。这些是框架用于高效 DOM 更新和依赖追踪的机制。错误在于开发者开始将业务规则和验证逻辑直接存入这些原语。状态管理器往往掩盖了泄漏抽象,允许 UI 组件直接依赖 store 结构。引入控制器和服务层,通过隔离业务逻辑与框架响应式系统来解决此问题。
服务器纪律的实际应用
采用后端方法无需放弃 React、Vue 或 Solid。这关乎在生态系统中组织代码。为了实现可预测性和降低新开发者的认知负担,只需遵循几条原则:
- 在应用边界严格验证传入事件和 API 数据(使用 Zod、Yup 或 TypeScript guards)。
- 在传递给业务层前,将原始服务器响应和用户输入转换为 DTO。
- 将领域逻辑隔离在纯服务中,这些服务不导入框架 hooks,且对 DOM 一无所知。
- 使用仓库抽象数据源(LocalStorage、IndexedDB、REST、GraphQL)。
- 控制器作为唯一连接点:将事件映射到服务调用,将结果映射到展示组件的 props。
这种方法使代码与框架无关。业务逻辑易于通过单元测试覆盖,无需渲染器模拟,从一种 UI 方案迁移到另一种仅影响薄适配层。
关键要点
- 前端应用在架构上等同于部署在浏览器中的 REST 服务器:事件取代 HTTP 请求,渲染形成响应。
- HTML 历史上实现了 HATEOAS 原则,同时向客户端交付状态和可用操作。
- 各种术语(stores、composables、atoms)制造了新奇幻觉,但往往模糊职责边界并复杂化维护。
- Hooks 和 signals 是框架基础设施原语,不是存放业务规则或复杂验证的地方。
- 应用服务器纪律(DTOs、controllers、services、repositories)可降低代码耦合、简化测试,并使架构独立于特定 UI 框架。
— Editorial Team
暂无评论。