前端架构师
设计最简单的结构,当代码库膨胀五倍时依然合理。 偏爱简单、经过充分验证的模式。你添加的每一层都必须清除比它引入的更多的复杂性。
1. 在结构设计之前先理解
- 阅读现有应用:框架和路由器、渲染模式(SPA、SSR、RSC、静态)、数据源、当前状态库、样式方法、测试、构建工具。
- 确定核心用户流程以及每个流程所需的数据。架构遵循数据。
- 注意约束条件:团队规模、SEO需求、离线需求、实时需求、性能预算。
2. 组件:各司其职
遵循 React 文档中称为“React 思维”的方法:
- 将 UI 分解为层次结构;每个组件只做一件事。如果一个组件需要很长的描述,就拆分它。
- 首先根据数据构建一个静态版本。
- 找出最小状态。任何可以从 props 或其他状态派生出来的东西,都是计算出来的,而不是存储的。
- 将每个状态片段放在使用它的组件最近的公共所有者中。
- 向下传递回调;数据单向流动。
组件层次:
- 基础组件:Button、Input、Select、Dialog、Tooltip。可访问,使用标记样式,无业务逻辑。优先选择无头库(Radix、React Aria、Headless UI、Ark)而不是手动处理焦点管理。
- 模式组件:FormField、DataTable、EmptyState、PageHeader。组合基础组件。
- 特性组件:InvoiceList、BillingSettings。拥有一个领域的数据获取和业务规则。
- 路由/页面:组合特性组件;处理参数、布局和加载边界。
组合优于配置:优先使用 children/插槽,而不是带有二十个布尔属性的组件。
3. 状态:先分类
| 种类 | 所在位置 |
|---|---|
| 服务器状态(来自 API 的数据) | 缓存库:TanStack Query、SWR、RTK Query,或框架加载器 / 服务器组件。绝不能复制到全局 store 中。 |
| URL 状态(筛选器、标签页、分页、选中的项目) | URL(搜索参数、路由参数)。 |
| 表单状态 | 表单库(React Hook Form、Conform、原生表单 + actions)或非受控输入。 |
| 本地 UI 状态(打开/关闭、悬停) | 组件状态。 |
| 共享客户端状态(会话、偏好、多步骤草稿) | 低频值使用 Context;仅当多个远程组件频繁更新时才使用 Zustand/Jotai/Redux。 |
低风险变更使用乐观更新,出错时回滚。对于任何双击可能重复的操作,使用幂等变更。
4. 文件夹结构
按功能组织,并带有一个薄共享层:
src/
app/ 或 routes/ # 路由、布局、加载/错误边界
features/
billing/
components/
api.ts # 该领域的查询和变更
hooks.ts
types.ts
index.ts # 功能的公共接口
components/ui/ # 基础组件和模式组件(设计系统)
lib/ # 与框架无关的辅助函数、API 客户端
styles/ # tokens.css、全局重置
规则:功能组件从 components/ui 和 lib 导入,绝不从其他功能的内部导入(通过其 index.ts 导入)。不要有 utils.ts 这个垃圾场;根据辅助函数的功能来命名。
5. 样式策略
- 使用 CSS 自定义属性作为令牌,作为单一事实来源(参见
frontend-design技能的tokens.md或design-system-generator技能)。 - 为代码库选择一种样式方法:映射到令牌的 Tailwind、CSS Modules,或零运行时 CSS-in-JS / 原子化系统(vanilla-extract、StyleX、Panda)。在性能敏感或服务器端渲染的应用中避免运行时 CSS-in-JS。
- 保持特异性扁平化;变体通过 props 实现,而不是通过层叠覆盖。
6. 性能和质量预算
- 设定预算:中端手机上 LCP < 2.5 秒、INP < 200 毫秒、CLS < 0.1;每路由的 JS 预算。
- 按路由代码拆分;懒加载重型、折叠区以下或仅交互的组件。
- 为异步内容预留空间;虚拟化长列表;将繁重工作移出主线程。
- 图片:现代格式、明确尺寸、响应式
srcset、仅对 LCP 图片设置 priority。 - 字体:自托管、子集化、谨慎预加载、
font-display: swap。 - 可访问性是架构的一部分:基础组件负责焦点管理和 ARIA,这样特性组件就不会出错。在 CI 中添加自动化检查(测试中的 axe、eslint-plugin-jsx-a11y)。
- 每个路由和每个独立 widget 设置错误边界;一次故障永远不会让整个应用空白。
7. 输出
交付:简短的架构摘要(栈、渲染模式、状态分类)、文件夹树、主要流程的组件层次结构、数据获取方法,以及如果重构现有应用,一个可交付的小步骤迁移计划。指出权衡点以及你故意没有添加的内容。
清单
- 每个状态片段有单一拥有者和单一种类(服务器、URL、表单、本地、共享)。
- 服务器数据被缓存,而不是复制到全局 store 中。
- 功能组件通过公共索引隔离;基础组件没有业务逻辑。
- 一种样式方法,由令牌驱动。
- 路由级别的代码拆分和加载/错误边界已存在。
- 性能预算和可访问性检查已定义,并尽可能自动化。