本章把前四章的知识汇总到专家能力:控制复杂度、做技术决策、设计演进路径、评审风险,并通过 Knowledge Hub 主线项目贯穿落地。
1. 五层能力模型
| 层级 | 能力目标 | 核心问题 |
|---|---|---|
| 入门 | 写组件、状态、事件、表单、列表 | 页面怎么拆?状态怎么更新? |
| 进阶 | Hook、副作用、Context、Reducer、异步数据 | 外部系统怎么同步?复杂状态怎么建模? |
| 高级 | 渲染机制、性能、Suspense、错误边界 | 为什么重新渲染?如何定位性能问题? |
| 精通 | TypeScript、测试、工程化、组件库、发布 | 团队如何长期稳定交付? |
| 专家 | 架构、领域模型、选型、演进、治理 | 如何控制复杂度和迁移成本? |
专家不是知道最多 API,而是能解释取舍。
2. 架构设计目标
架构解决四类复杂度:
- 业务复杂度:规则、流程、权限、协作。
- 技术复杂度:性能、数据同步、SSR、跨端。
- 团队复杂度:多人协作、模块边界、组件复用。
- 时间复杂度:需求变化、技术债、迁移成本。
3. 分层模型
text
app 应用启动、Provider、路由
pages 页面编排、路由参数
features 业务功能模块
entities 领域实体
shared 通用基础能力
依赖方向:
text
app -> pages -> features -> entities -> shared
下层不应该依赖上层。
4. Feature-first 目录
text
features/knowledge-hub/
├── components/
│ ├── KnowledgeCard.tsx
│ ├── KnowledgeFilters.tsx
│ └── KnowledgeHubView.tsx
├── model/
│ ├── reducer.ts
│ ├── selectors.ts
│ └── types.ts
├── services/
│ └── knowledgeApi.ts
├── hooks/
│ └── useKnowledgeHub.ts
└── index.ts
优点:
- 同一业务的 UI、状态、服务靠近。
- 修改需求不需要跨很多目录。
- 对外暴露稳定入口。
5. 领域模型
业务规则不应散落在 JSX 中。
错误:
jsx
{stage.order === 1 || completedStages.includes(stage.order - 1) ? (
<StartButton />
) : (
<LockedView />
)}
更好:
ts
function canStartStage(progress: Progress, stage: Stage) {
return stage.order === 1 || progress.completedStages.includes(stage.order - 1);
}
组件:
jsx
const unlocked = canStartStage(progress, stage);
return unlocked ? <StartButton /> : <LockedView />;
6. 状态分层
text
UI state 弹窗、输入、hover
Page state 当前筛选、分页
Domain state 购物车、学习进度、审批状态
Server state 接口数据、缓存、失效
App state 用户、主题、语言
不要把所有状态塞到一个 store。
7. 服务边界
text
component -> hook -> service -> api client
示例:
ts
export function useCompleteLesson() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: completeLesson,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['lessons'] });
},
});
}
组件只使用业务 Hook:
jsx
const completeLesson = useCompleteLesson();
8. Provider 设计
jsx
function AppProviders({ children }) {
return (
<ThemeProvider>
<AuthProvider>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</AuthProvider>
</ThemeProvider>
);
}
Provider 适合全局配置,不适合塞所有业务状态。
9. SSR、SSG、CSR 选型
CSR 适合:
- 登录后后台系统。
- SEO 不重要。
- 强交互应用。
SSR 适合:
- SEO 重要。
- 首屏体验要求高。
- 内容依赖请求但需要服务端直出。
SSG 适合:
- 内容稳定。
- 文档、博客、营销页。
- 构建时生成即可。
10. 微前端适用条件
适合:
- 多团队独立发布。
- 历史系统渐进迁移。
- 应用边界清晰。
不适合:
- 单团队单应用。
- 只是想拆目录。
- 共享状态和设计系统还没治理好。
11. ADR 架构决策记录
md
# ADR: 使用 TanStack Query 管理服务端状态
## 背景
课程、学习进度、用户信息在多个页面复用,且需要缓存、重试和失效。
## 决策
使用 TanStack Query 管理服务端状态,本地 UI 状态继续使用 useState/useReducer。
## 后果
优点:减少重复请求逻辑,统一 loading/error。
代价:团队需要学习 queryKey 和缓存失效策略。
12. Knowledge Hub 项目背景
团队需要一个内部知识库:
- 管理 React 学习模块。
- 支持搜索和层级筛选。
- 记录收藏和完成状态。
- 未来接入登录、接口、权限和统计。
当前项目入口:
text
src/projects/knowledge-hub/KnowledgeHub.jsx
src/projects/knowledge-hub/knowledgeHubReducer.js
13. Stage 1:静态 UI
目标:把数据变成组件树。
jsx
function KnowledgeCard({ item }) {
return (
<article>
<h3>{item.title}</h3>
<p>{item.summary}</p>
<TagList tags={item.tags} />
</article>
);
}
训练点:
- JSX。
- 组件拆分。
- Props。
- 列表 key。
14. Stage 2:交互状态
jsx
const [query, setQuery] = useState('');
const [level, setLevel] = useState('全部');
const [favorites, setFavorites] = useState([]);
训练点:
useState。- 受控组件。
- 派生数据。
- 事件回调。
15. Stage 3:Reducer
jsx
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'favorite-toggled', id: item.id });
Reducer:
js
function reducer(state, action) {
switch (action.type) {
case 'favorite-toggled':
return toggleFavorite(state, action.id);
case 'completed-toggled':
return toggleCompleted(state, action.id);
default:
return state;
}
}
训练点:
- 业务动作。
- 状态转移。
- 不可变更新。
- 可测试逻辑。
16. Stage 4:Selector
js
function selectVisibleItems(items, state) {
return items.filter((item) => {
const matchLevel = state.level === '全部' || item.level === state.level;
const matchQuery = item.title.includes(state.query);
return matchLevel && matchQuery;
});
}
训练点:
- 派生数据抽离。
- 组件变薄。
- 未来迁移状态库更容易。
17. Stage 5:服务端数据
jsx
function KnowledgeHub() {
const itemsQuery = useKnowledgeItems();
const progressMutation = useUpdateProgress();
if (itemsQuery.isLoading) return <Loading />;
if (itemsQuery.isError) return <ErrorView />;
return (
<KnowledgeHubView
items={itemsQuery.data}
onComplete={progressMutation.mutate}
/>
);
}
训练点:
- 服务端状态。
- loading/error/empty。
- mutation。
- 缓存失效。
18. Stage 6:架构化
text
features/knowledge-hub/
├── components/
├── model/
├── services/
├── hooks/
└── index.ts
训练点:
- 模块边界。
- 领域模型。
- 服务层。
- 对外稳定 API。
19. Stage 7:专家级演进
未来扩展:
- 接入用户系统:AuthProvider。
- 接入服务端状态:TanStack Query。
- 支持路由:课程详情页和学习路径页。
- 支持权限:管理员编辑、普通用户学习。
- 支持统计:学习完成率、热门知识点。
- 支持设计系统:统一按钮、表单、弹窗、表格。
- 支持测试:Reducer、组件交互、E2E 主流程。
- 支持 SSR/RSC:内容型页面服务端渲染,交互组件客户端化。
20. React 反模式
直接修改 State:
jsx
items.push(newItem);
setItems(items);
派生状态重复存储:
jsx
const [total, setTotal] = useState(0);
useEffect(() => setTotal(items.length), [items]);
Effect 滥用:
- 没有外部系统时通常不需要 Effect。
缺失依赖:
jsx
useEffect(() => {
load(id);
}, []);
数组索引 key:
- 排序、删除、插入时会出错。
Context 塞所有状态:
- 渲染范围过大,模块耦合。
到处 useCallback:
- 没有 memo 子组件或依赖稳定需求时收益低。
布尔 Props 爆炸:
jsx
<Modal isDelete isAdmin isCompact useNewFooter />
21. 调试顺序
遇到 UI 异常,按顺序排查:
- 数据是否正确。
- Props 是否传对。
- State 是否被直接修改。
- key 是否稳定。
- Effect 依赖是否完整。
- 是否存在竞态请求。
- 是否因为 memo 缓存了旧值。
闭包旧值:
jsx
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
修正:
jsx
setCount((current) => current + 1);
22. Code Review 清单
组件:
- Props 是否表达业务语义?
- 是否直接修改 State?
- key 是否稳定?
- 是否有不必要的状态提升?
Hook:
- 依赖数组是否完整?
- Effect 是否真的需要?
- 清理函数是否存在?
- 自定义 Hook 是否隐藏复杂副作用?
状态:
- 派生数据是否重复存储?
- Reducer action 是否有业务语义?
- 服务端状态是否误放客户端 store?
性能:
- 是否有大列表?
- 是否有昂贵计算?
- 是否盲目 memo?
- 是否有包体积风险?
架构:
- 模块依赖方向是否清晰?
- 业务规则是否散落在 UI?
- 公共组件是否过度耦合业务?
- 是否有迁移和回滚路径?
23. 上线检查
- build 通过。
- lint 通过。
- typecheck 通过。
- 核心测试通过。
- 监控接入。
- 回滚方案明确。
- 环境变量校验。
- 包体积可控。
- 权限由服务端校验。
- 错误边界覆盖关键区域。
24. 专家判断题
如果能清晰回答这些问题,才接近专家:
- 为什么这个状态放在这里?
- 为什么不引入状态库?
- 为什么这个页面需要或不需要 SSR?
- 这个组件未来如何扩展?
- 这个优化如何证明有效?
- 这个架构决策失败后如何迁移?
- 当前方案 6 到 18 个月后还能否承载业务变化?
25. 专家思维
专家不会永远选择最复杂方案。专家会问:
- 当前问题的真实复杂度是什么?
- 最小可行方案是什么?
- 哪个决策未来最难迁移?
- 这个抽象是否真的降低复杂度?
- 团队是否有能力维护这个方案?
能解释取舍、控制风险、设计演进路径,才是专家能力。
26. 架构场景与演进扩展
后台管理系统:
- 登录后使用。
- SEO 不重要。
- 表格、筛选、表单多。
- 权限复杂。
推荐 Vite + React Router + TanStack Query;复杂客户端领域状态再引入 Zustand 或 Redux Toolkit;组件库和权限指令要早做治理。
内容站:
- SEO 重要。
- 首屏重要。
- 内容相对稳定。
推荐 Next.js、SSG/SSR、图片优化、CDN 缓存。不要把后台系统的架构套到内容站,也不要把内容站的 SSR 复杂度强加给后台。
高交互编辑器:
- 本地状态复杂。
- 操作历史。
- 快捷键。
- 大量局部更新。
推荐状态模型先行、Command pattern、undo/redo、局部 store、性能分析,必要时使用 Web Worker。
27. 技术债分类和治理
技术债包括:
- 认知债:没人知道为什么这样写。
- 结构债:模块边界混乱。
- 测试债:关键逻辑无法安全修改。
- 依赖债:库过期或无人维护。
- 性能债:没有指标和预算。
- 设计债:组件和视觉不一致。
- 发布债:无法快速回滚。
治理方式:
- ADR。
- 重构预算。
- 废弃计划。
- 自动化检查。
- 文档和 owner。
28. ADR 模板扩展
md
# ADR-0001: 标题
## 状态
Proposed / Accepted / Deprecated / Superseded
## 背景
为什么现在要做决策?
## 备选方案
1. 方案 A
2. 方案 B
3. 方案 C
## 决策
选择哪个方案,为什么?
## 后果
优点、代价、风险、迁移路径。
## 回滚
什么情况下撤销,如何撤销?
29. 专家代码评审模板
text
结论:是否可合入?
主要风险:行为、性能、安全、架构、测试。
必须修改:阻塞上线的问题。
建议修改:可后续优化的问题。
验证:已跑哪些测试?缺哪些测试?
后续:是否需要 ADR、监控、清理任务?
30. 专家知识点总表
- React declarative model。
- JSX 编译。
- 组件纯度。
- Props API。
- State 快照。
- 不可变更新。
- Reducer。
- Context。
- Effect 同步。
- 自定义 Hook。
- 外部 store。
- 服务端状态。
- Suspense。
- Error Boundary。
- Concurrent Rendering。
- SSR。
- Hydration。
- RSC。
- Server Functions。
- React Compiler。
- 性能预算。
- TypeScript 领域建模。
- 测试金字塔。
- 设计系统。
- 权限。
- 安全。
- 发布。
- 监控。
- ADR。
- 技术债治理。
31. 架构能力索引
- 分层架构。
- Feature-first。
- 领域模型。
- 状态分层。
- 服务层。
- API DTO 映射。
- 权限模型。
- 错误边界。
- 缓存策略。
- 离线策略。
- SSR/SSG/CSR 选型。
- RSC 边界。
- 微前端边界。
- 设计系统治理。
- 技术债治理。
- ADR。
- 发布回滚。
- 可观测性。
- 性能预算。
- 团队协作模型。
32. 架构失败信号
- 新人无法定位业务代码。
- 修改一个字段牵动十几个文件。
- 所有东西都依赖 shared。
- 组件库没人敢升级。
- 权限判断散落各处。
- 接口字段直接进入 UI。
- 状态库里混着服务端数据和 UI 状态。
- 没有测试保护核心业务。
- 没有 ADR,没人知道选型原因。
- 无法快速回滚。
33. 专家决策公式
text
决策质量 = 问题匹配度 + 可解释性 + 可迁移性 + 可验证性 - 长期维护成本
做技术决策时,至少写清:
- 当前问题。
- 为什么现在解决。
- 备选方案。
- 为什么选这个。
- 失败时如何回退。
- 未来何时重新评估。
面试题完整答案总集:架构与专家实践
为什么这个状态放在这里?
状态位置由使用范围、业务含义和生命周期决定。只被一个组件使用就放组件内;兄弟组件共享放最近公共父组件;业务流程状态放 feature model;服务端数据放请求缓存;主题、语言、用户放 Provider。状态放得越高,影响范围和耦合越大。
为什么不引入状态库?
如果当前状态只在单页或单 feature 内部协作,useState、useReducer 和 Context 足够。状态库会引入额外概念、依赖和团队成本。只有出现多页面共享、细粒度订阅、复杂领域状态、DevTools 需求或 Context 性能问题时,引入状态库才更合理。
为什么这个页面需要或不需要 SSR?
需要 SSR 的原因通常是 SEO、首屏性能、内容直出或社交分享。不需要 SSR 的典型场景是登录后后台系统、强交互应用、SEO 不重要、客户端渲染已满足指标。SSR 会增加服务端成本、缓存复杂度和 hydration 问题,所以应基于业务指标决定。
这个组件未来如何扩展?
看组件 API 是否表达稳定语义,内部实现是否可替换,变化点是否被隔离。可扩展组件通常有清晰职责、合理 variant、组合能力和明确废弃策略。不能靠不断增加布尔 Props 扩展,否则状态组合会失控。
这个优化如何证明有效?
必须有优化前后对比指标,例如 Profiler 渲染时间、交互延迟、FPS、包体积、LCP、INP。没有测量的优化只是猜测。优化应写明问题、根因、修改、验证结果和防回退措施。
这个架构决策失败后如何迁移?
专家决策要包含退出方案。例如状态库选错时是否有 selector/hook 适配层,SSR 成本过高时能否回退 CSR,组件库 API 废弃时是否能渐进迁移。没有迁移路径的决策风险很高,应通过 ADR 记录回退条件。
当前方案 6 到 18 个月后还能否承载业务变化?
要从业务增长、团队规模、数据复杂度、性能目标和发布频率评估。如果模块边界清晰、状态分层合理、测试保护关键逻辑、依赖可替换、发布可回滚,就更可能承载变化。若已经出现共享层膨胀、业务规则散落、无测试、无法定位问题,说明需要重构。
什么是专家级前端架构能力?
专家级能力不是使用最复杂技术,而是能识别真实复杂度,选择足够简单的方案,并为未来变化留下迁移路径。它包括领域建模、状态分层、性能预算、工程质量、团队协作、技术债治理和架构决策记录。