Skip to content

数据与状态管理 面试题

本题库共收录 79 道面试题(基础 16 / 进阶 28 / 深入 23 / 架构 12)。 本文件收录数据与状态管理相关面试题,目标题量 30 道。 题型覆盖:概念题、代码分析题、手写代码题、场景设计题、系统设计题、框架原理题。 难度覆盖:基础、进阶、深入、架构。 每道题除标准参考答案外,另附口头回答版,便于面试时快速组织语言。

目录


基础题(8 道)

FB-29-CO-B-001:什么是客户端状态和服务端状态?它们有什么区别?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:客户端状态、服务端状态、状态分类、数据源 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请解释客户端状态和服务端状态的概念,并说明它们在前端应用中的区别与管理方式。

参考答案

  • 客户端状态(Client State):指仅存在于浏览器端、由前端自己产生和维护的数据。例如 UI 开关、主题、表单输入、路由状态、本地缓存等。它的生命周期由前端控制,关闭页面后通常会丢失(除非持久化)。
  • 服务端状态(Server State):指来源于服务端、反映服务器真实数据的状态。例如用户信息、订单列表、商品详情等。它有权威性(source of truth),前端需要通过网络请求获取并同步。

核心区别:

维度客户端状态服务端状态
来源前端自身产生服务端 API
权威性本地有效服务端为唯一真相源
同步方式本地即时更新异步请求、缓存、重新验证
生命周期页面关闭可丢失持久化在服务端
典型库Redux、Zustand、PiniaReact Query、SWR、TanStack Query

最佳实践:

  • 不要把服务端状态无差别地放入全局状态管理库。
  • 使用专门的服务端状态管理库处理缓存、失效、重新验证。
  • 客户端状态按作用域决定放在组件本地还是全局。

评分维度

  • 能清晰区分两类状态的来源和权威性(40%)
  • 能说明生命周期和同步方式的差异(30%)
  • 能举例说明典型场景和对应管理工具(30%)

常见错误

  • 把所有 API 数据都塞入 Redux / Pinia,导致缓存和同步逻辑重复实现。
  • 认为 Redux 不适合放服务端状态,完全否定其适用场景。
  • 混淆本地缓存(localStorage)和服务端状态。

延伸追问

  • 如果一个状态既是服务端状态又被前端修改,应该怎么管理?
  • 表单草稿属于客户端状态还是服务端状态?

相关题目

参考资源

口头回答版

客户端状态是前端自己产生的,比如主题、弹窗开关、表单输入,关掉页面就没了;服务端状态是服务器返回的,比如用户资料、订单列表,服务器是唯一的真相源。管理方式是:客户端状态用 Redux、Zustand、Pinia 这类状态库;服务端状态最好用 React Query、SWR 这种专门处理缓存、失效和重新验证的库。不要把所有 API 数据都塞到全局状态库里。


FB-29-CO-B-002:全局状态和局部状态有什么区别?如何选择作用域?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:全局状态、局部状态、状态作用域、状态提升 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明全局状态和局部状态的区别,并给出选择状态作用域的原则。

参考答案

  • 局部状态(Local State):仅在某个组件或其子树内使用,通过 useStateref、组件 props 等管理。数据流清晰、影响范围小、容易测试和复用。
  • 全局状态(Global State):跨多个组件、路由或模块共享,通常通过 Context、状态管理库或 URL 参数管理。

选择原则:

  1. 从局部开始:先放在离使用最近的组件,避免过早全局化。
  2. 需要共享再提升:当多个 sibling 组件需要同一状态时,提升到最近的公共父组件。
  3. 跨路由/模块共享时放入全局:例如用户登录态、权限、主题。
  4. 服务端状态单独管理:不要和无脑放入 Redux,优先使用 TanStack Query / SWR。
  5. URL 状态交给路由:可分享、可回退的状态,例如筛选条件、分页、当前 tab。

示例:

tsx
// 局部状态:仅当前表单组件使用
const [formValues, setFormValues] = useState({ name: '' });

// 全局状态:登录用户信息,多个页面共享
const useUserStore = create(() => ({ user: null }));

评分维度

  • 能区分全局与局部状态的影响范围(40%)
  • 能说出“从局部开始、按需提升”的原则(30%)
  • 能举例说明不同作用域的典型状态(30%)

常见错误

  • 所有状态一开始就放入全局 Store。
  • 把只在一个组件里使用的状态也通过 Redux 管理。
  • 忽略 URL 作为状态容器的作用。

延伸追问

  • 状态提升和 Context 有什么关系?
  • 什么时候应该把全局状态下沉到局部?

相关题目

参考资源

口头回答版

局部状态只在某个组件里用,比如一个开关、一个输入框;全局状态是跨组件甚至跨路由共享的,比如用户信息、主题。选择原则是从局部开始,哪个组件用就放在哪;多个组件需要就提升到公共父组件;跨路由或模块再用全局 Store。别一上来就把所有状态都塞全局,服务端状态还要单独管理。


FB-29-CO-B-003:Redux 的核心概念有哪些?简述它的数据流

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:Redux、Store、Action、Reducer、单向数据流 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明 Redux 的核心概念(Store、Action、Reducer),并描述一次完整的数据更新流程。

参考答案

Redux 的核心概念:

  1. Store:单一的状态树,保存整个应用的 state,提供 getState()dispatch()subscribe()
  2. Action:描述发生了什么的普通对象,必须包含 type 字段,可携带 payload。
  3. Reducer:纯函数,接收当前 state 和 action,返回新的 state。(state, action) => newState
  4. Dispatch:触发 action 的方法,store.dispatch(action)

数据流:

text
View -> dispatch(Action) -> Reducer -> new State -> View re-render

完整示例:

ts
// Action
const increment = (payload) => ({ type: 'INCREMENT', payload });

// Reducer
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + action.payload;
    default:
      return state;
  }
};

// Store
const store = createStore(counterReducer);

// View
document.getElementById('add').addEventListener('click', () => {
  store.dispatch(increment(1));
});

特点:

  • 单一数据源、state 只读、使用纯函数修改。
  • 数据流单向、可预测、便于调试和回溯。

评分维度

  • 能准确说出 Store、Action、Reducer 的定义(50%)
  • 能按顺序描述 dispatch 到 view 更新的完整流程(30%)
  • 能写出或口述一个简单的 reducer 示例(20%)

常见错误

  • 在 reducer 中直接修改原 state。
  • 把异步逻辑写在 reducer 中。
  • 认为 Redux 必须配合 React 使用。

延伸追问

  • Redux 为什么要强调不可变更新?
  • Redux Toolkit 如何简化样板代码?

相关题目

参考资源

口头回答版

Redux 核心是 Store、Action、Reducer。Store 是保存全局状态的容器;Action 是一个带 type 的普通对象,描述发生了什么事;Reducer 是纯函数,根据当前 state 和 action 返回新的 state。数据流是:组件 dispatch 一个 action,reducer 处理后返回新 state,然后组件重新渲染。Redux 强调单一数据源、state 只读、用纯函数修改。


FB-29-CO-B-004:Pinia 和 Vuex 有什么区别?为什么 Vue 3 推荐 Pinia?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理、16 Vue 标签:Pinia、Vuex、Vue 3、状态管理、Composition API 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请对比 Pinia 和 Vuex,并说明 Pinia 为什么成为 Vue 3 官方推荐的状态管理方案。

参考答案

维度Vuex 3/4Pinia
API 风格Options API 风格(state/mutations/actions/getters)Composition API 风格,直接用 state/actions/getters
mutations必须同步,通过 mutations 修改 state无 mutations,action 中可直接修改 state
TypeScript类型支持较弱,需要额外封装原生 TypeScript 支持,类型推导好
模块拆分需要 namespaced 模块每个 Store 天然是独立模块
体积较大更轻量
与 Vue 3支持但设计偏旧官方推荐,设计贴合 Composition API
Devtools支持支持更好

Pinia 示例:

ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  const double = computed(() => count.value * 2);
  const increment = () => count.value++;

  return { count, double, increment };
});

Vue 3 推荐 Pinia 的原因:

  • 更好的 TypeScript 支持。
  • API 更简洁,没有 mutations 的样板代码。
  • 与 Composition API / script setup 天然契合。
  • 模块化和 tree-shaking 更友好。

评分维度

  • 能说出 Pinia 无 mutations、API 更简洁(40%)
  • 能对比 TS 支持和模块拆分方式(30%)
  • 能说明与 Vue 3 Composition API 的契合度(30%)

常见错误

  • 认为 Pinia 完全不支持 Options API(实际是支持的)。
  • 认为 Pinia 不能用于 Vue 2(Pinia 同时支持 Vue 2 和 Vue 3)。
  • 在 Pinia 的 action 里仍然写 mutation 风格的代码。

延伸追问

  • Pinia 的 Store 如何做持久化?
  • Pinia 的 action 支持 async 吗?和 Vuex 的 actions 有什么区别?

相关题目

参考资源

口头回答版

Pinia 是 Vue 3 官方推荐的状态管理库。和 Vuex 比,Pinia 没有 mutations,action 里可以直接改 state,代码更简洁;TypeScript 支持也更好;每个 Store 天然是独立模块,不需要 namespaced。Vuex 必须用 mutations 同步修改 state,类型支持也相对弱。所以 Vue 3 项目一般直接用 Pinia,配合 Composition API 很舒服。


FB-29-CO-B-005:Zustand 和 Redux 相比有什么优势和劣势?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理、15 React 标签:Zustand、Redux、状态管理、React、轻量 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请对比 Zustand 和 Redux,说明它们各自的适用场景。

参考答案

Zustand 是一个轻量、简单的 React 状态管理库;Redux 是更成熟、生态更丰富的可预测状态容器。

维度ReduxZustand
学习曲线较陡,需要理解 Action/Reducer/Middleware平缓,几行代码即可使用
样板代码多( especially 原始 Redux)极少
TypeScriptRedux Toolkit 已大幅改善原生优秀
中间件生态丰富(redux-thunk、redux-saga、RTK Query 等)较少,但常用功能内置
调试工具Redux DevTools 成熟支持 Redux DevTools 中间件
适用规模大型应用、复杂状态逻辑中小型应用、局部/轻量全局状态
不可变性强制不可变推荐不可变,但不强制

Zustand 示例:

ts
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

// 组件中使用
const count = useStore((state) => state.count);

选型建议:

  • 大型应用、需要严格数据流、时间旅行调试、复杂异步流:选 Redux Toolkit。
  • 中小型应用、希望快速上手、状态逻辑不太复杂:选 Zustand。
  • 两者可以共存,局部状态用 Zustand,复杂全局状态用 Redux Toolkit。

评分维度

  • 能从学习曲线、样板代码、生态三个维度对比(50%)
  • 能写出 Zustand 基础用法(30%)
  • 能给出选型建议(20%)

常见错误

  • 认为 Zustand 完全替代 Redux(大型复杂场景 Redux 仍有优势)。
  • 在 Zustand 中直接修改 state 而不使用 set。
  • 选择状态库时不考虑团队规模和长期维护成本。

延伸追问

  • Zustand 如何实现状态的按需订阅?
  • Zustand 如何处理异步 action?

相关题目

参考资源

口头回答版

Zustand 比 Redux 轻量很多,几行代码就能跑起来,学习成本低,TypeScript 支持也好;Redux 生态更丰富,中间件多,适合大型复杂应用。Zustand 用 create 创建 store,组件里用 selector 按需订阅。一般来说,中小型项目或局部状态用 Zustand,大型项目、需要严格数据流和时间旅行调试的用 Redux Toolkit。两者不是完全互斥的。


FB-29-CO-B-006:React Query / TanStack Query 主要解决什么问题?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理、15 React 标签:React Query、TanStack Query、服务端状态、缓存、SWR 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明 React Query(TanStack Query)主要解决什么问题,以及它与传统状态管理库的核心区别。

参考答案

React Query 是专门管理服务端状态的库,核心解决以下问题:

  1. 数据获取:简化请求、loading、error 状态管理。
  2. 缓存:避免重复请求,提升性能和用户体验。
  3. 失效与重新验证(Stale-while-revalidate):在缓存过期或窗口重新聚焦时自动刷新。
  4. 后台更新:用户无感知地保持数据新鲜。
  5. 分页、无限滚动、乐观更新、去重:内置常见数据请求模式。
  6. devtools:可视化缓存状态和查询生命周期。

与传统状态管理库(Redux、Zustand、Pinia)的区别:

维度Redux / Zustand / PiniaReact Query
管理对象客户端状态服务端状态
数据同步手动自动缓存 + 重新验证
缓存策略无内置强大的缓存和失效策略
异步处理需要 thunk / saga 等内置
目标全局状态共享服务端数据获取与同步

基础示例:

tsx
import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  const { data, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then((res) => res.json()),
  });

  if (isLoading) return <p>加载中...</p>;
  if (error) return <p>出错了</p>;
  return <div>{data.name}</div>;
}

评分维度

  • 能说出 React Query 主要解决服务端状态管理问题(40%)
  • 能列举缓存、失效、后台刷新等核心能力(30%)
  • 能与传统状态管理库区分管理对象(30%)

常见错误

  • 用 React Query 管理所有状态,包括 UI 开关等客户端状态。
  • 忽略 queryKey 的设计,导致缓存失效问题。
  • 认为 React Query 可以完全替代 Redux。

延伸追问

  • queryKey 的设计原则是什么?
  • React Query 的 staleTimegcTime 有什么区别?

相关题目

参考资源

口头回答版

React Query 主要解决服务端状态管理问题。传统状态库比如 Redux 是管客户端状态的,React Query 是专门管 API 数据的:帮你做缓存、loading、error、失效重新验证、后台刷新、分页、乐观更新这些。它和传统状态库不是替代关系,而是互补。用 useQuery 时传 queryKey 和 queryFn,它会自动帮你缓存和刷新。


FB-29-CO-B-007:SWR 是什么?它和 React Query 有什么区别?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理、15 React 标签:SWR、React Query、缓存、数据获取、Next.js 出现频率:中频 预计回答时长:3-5 分钟

题目描述: 请解释 SWR 的核心思想,并对比 SWR 和 React Query 的差异。

参考答案

SWR(stale-while-revalidate)是 Vercel 推出的数据获取库,名字本身就来自 HTTP 缓存策略:先返回缓存(stale)数据,同时在后台重新验证(revalidate)最新数据。

核心思想:

  1. 优先使用缓存:页面首次加载或返回时,先展示缓存数据,减少白屏。
  2. 后台自动重新验证:在合适的时机(窗口聚焦、网络恢复、定时器)自动刷新。
  3. 极简 API:主要以 useSWR(key, fetcher) 为核心。
  4. 与 Next.js 生态深度集成:适合 SSR/SSG 场景。

SWR 与 React Query 对比:

维度SWRReact Query
体积更轻量稍大
API 简洁度更简洁更完整
功能丰富度基础功能完善,高级功能较少更丰富(mutation、devtools、离线等)
生态/工具DevTools 较弱DevTools 强大
适用场景简单数据同步、Next.js 项目复杂服务端状态管理

SWR 示例:

tsx
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher);

  if (isLoading) return <div>loading</div>;
  if (error) return <div>failed</div>;
  return <div>hello {data.name}</div>;
}

评分维度

  • 能解释 SWR 名字来源和核心思想(40%)
  • 能对比 SWR 与 React Query 的简洁度和功能丰富度(30%)
  • 能写出基础 useSWR 用法(30%)

常见错误

  • 认为 SWR 只能用于 Next.js。
  • 把 SWR 当作全局状态管理库使用。
  • 在 mutation 场景下仍用 SWR 而不用 useSWRMutation

延伸追问

  • SWR 的 revalidateOnFocus 参数有什么用?
  • 如果 SWR 和 React Query 都可以满足需求,如何选型?

相关题目

参考资源

口头回答版

SWR 是 Vercel 出的数据获取库,名字来自 HTTP 缓存策略 stale-while-revalidate:先拿缓存数据给用户看,同时在后台去拉最新数据。核心 API 是 useSWR(key, fetcher)。它和 React Query 都是管服务端状态的,SWR 更轻量、API 更简单,适合 Next.js 或需求不复杂的项目;React Query 功能更全,有强大的 devtools、mutation、离线支持,适合复杂场景。


FB-29-CO-B-008:表单状态为什么要单独管理?受控与非受控表单如何选择?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:表单状态、受控组件、非受控组件、React Hook Form、Formik 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明表单状态为什么值得单独抽象和管理,并解释受控表单与非受控表单的区别和选择依据。

参考答案

表单状态之所以需要单独管理,是因为它具备以下特点:

  1. 高频变更:每个输入框都会触发更新,放在全局状态容易导致大范围重渲染。
  2. 局部性:表单状态通常只在表单组件内部使用,天然适合局部管理。
  3. 校验与格式化:需要实时校验、错误提示、字段联动、格式化。
  4. 提交语义:表单有初始化、校验、提交、重置等明确生命周期。
  5. 性能敏感:不必要的重渲染会显著影响输入体验。

受控表单 vs 非受控表单:

维度受控表单非受控表单
数据来源React/Vue stateDOM 自身
更新方式onChange 更新 stateref 读取 DOM 值
实时校验容易需要手动
性能每次输入触发渲染更少渲染
典型库Formik、React Final Form、@mantine/formReact Hook Form
适用场景复杂联动、实时校验大型表单、性能敏感

示例(受控):

tsx
const [value, setValue] = useState('');
<input value={value} onChange={(e) => setValue(e.target.value)} />;

示例(非受控,React Hook Form):

tsx
const { register, handleSubmit } = useForm();
<input {...register('name')} />;

选择依据:

  • 简单表单或需要实时联动:受控。
  • 大型表单、字段多、性能敏感:非受控(React Hook Form)。

评分维度

  • 能说明表单状态高频、局部、校验等特点(40%)
  • 能区分受控和非受控表单的数据来源与更新方式(30%)
  • 能给出选择依据和典型库(30%)

常见错误

  • 所有表单状态都放到全局 Store。
  • 在大型表单中仍使用完全受控,导致输入卡顿。
  • 认为非受控表单不能做校验。

延伸追问

  • React Hook Form 的性能优势来自哪里?
  • 文件上传框为什么通常用非受控?

相关题目

参考资源

口头回答版

表单状态要单独管理,因为它更新很频繁,而且基本只在表单组件里用。放全局 Store 会让很多无关组件重新渲染。表单分受控和非受控:受控是每个输入都绑定 state,onChange 更新,适合做实时校验和联动;非受控是 DOM 自己保存值,用 ref 或 React Hook Form 去取,性能更好,适合字段很多的大型表单。


进阶题(8 道)

FB-29-CO-A-009:SWR 缓存策略是什么?stale-while-revalidate 如何在前端应用?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:SWR、stale-while-revalidate、缓存策略、React Query、数据新鲜度 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请解释 SWR(stale-while-revalidate)缓存策略的原理,并说明它在前端数据管理中的应用场景和注意事项。

参考答案

SWR 是一种 HTTP 缓存控制策略,也被 React Query、SWR 等库借鉴为数据同步策略。其核心思想是:

  1. Stale(陈旧数据优先):当缓存存在但可能已过期时,先立即返回缓存数据给用户。
  2. Revalidate(后台重新验证):同时异步发起请求去服务端校验/获取最新数据。
  3. 更新 UI:后台请求完成后,如果数据有变化,再更新界面。

流程图:

text
用户请求数据


缓存是否存在?

  ├─ 是 ──▶ 立即返回缓存(stale)
  │           │
  │           ▼
  │        后台发起请求(revalidate)
  │           │
  │           ▼
  │        数据变化则更新 UI

  └─ 否 ──▶ 发起请求,等待最新数据

应用场景:

  • 首页feed、商品列表、用户资料等容忍短暂不一致的数据。
  • 用户返回页面或切换 tab 时,避免白屏。
  • 网络抖动或弱网环境下提升可用性。

注意事项:

  • 数据新鲜度要求:金融支付、库存扣减等对一致性要求极高的场景不适合单纯 SWR。
  • 错误处理:后台 revalidate 失败时,要有降级策略(toast、重试按钮)。
  • 更新冲突:用户正在编辑时,后台刷新可能覆盖用户输入。

示例(React Query 中配置 SWR 行为):

tsx
const { data } = useQuery({
  queryKey: ['posts'],
  queryFn: fetchPosts,
  staleTime: 1000 * 60 * 5, // 5 分钟内视为 fresh
  refetchOnWindowFocus: true,
});

评分维度

  • 能准确解释 stale-while-revalidate 两个阶段的含义(40%)
  • 能说明适用场景和不适合的场景(30%)
  • 能指出数据一致性、错误处理等注意事项(30%)

常见错误

  • 把 SWR 策略用于所有数据,忽略强一致性需求。
  • 认为 stale 数据一定是错误数据。
  • 不配置 staleTime / refetch 策略,导致请求过于频繁。

延伸追问

  • 如果后台 revalidate 返回的数据和用户当前编辑冲突,怎么处理?
  • SWR 策略和强制刷新(hard refresh)如何结合?

相关题目

参考资源

口头回答版

SWR 就是 stale-while-revalidate:先拿缓存里的旧数据给用户看,不让页面白屏,同时后台悄悄去服务器拉最新数据,拉到了再更新界面。React Query 和 SWR 库都是这个思路。适合对实时性要求不高的场景,比如商品列表、用户资料。但像支付、库存这种强一致性场景不能光靠 SWR,要结合强制刷新或乐观更新。


FB-29-CO-A-010:前端缓存失效策略有哪些?如何选择?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:缓存失效、缓存策略、React Query、SWR、数据一致性 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请列举前端常见的缓存失效策略,并说明在不同业务场景下如何选择。

参考答案

常见缓存失效策略:

  1. TTL(Time To Live)

    • 给缓存设置固定过期时间,过期后重新请求。
    • 适用于更新频率可预测的数据,如配置项、分类列表。
  2. Stale-While-Revalidate(SWR)

    • 先返回缓存,后台异步刷新。
    • 适用于能容忍短暂不一致的展示型数据。
  3. 事件驱动失效(Event-based Invalidation)

    • 当某个操作(mutation)成功后,主动使相关 queryKey 的缓存失效。
    • 例如创建订单后,失效订单列表缓存。
  4. 乐观更新(Optimistic Update)

    • 先更新 UI,再发请求,失败则回滚。
    • 属于“写”侧缓存策略,常与 SWR 配合使用。
  5. 手动刷新 / 强制刷新

    • 提供刷新按钮或下拉刷新,明确重新获取数据。
    • 适合用户主动触发或强一致性场景。
  6. 基于版本的缓存(Version-based)

    • 服务端返回数据版本号或 ETag,前端对比后决定是否使用缓存。
    • 适合需要精确控制一致性的场景。
  7. 窗口聚焦/网络恢复重新验证

    • 用户切换回页面或网络恢复时自动刷新。

选择依据:

场景推荐策略
商品列表、资讯 feedSWR + 窗口聚焦刷新
用户配置、字典数据TTL(较长)
创建/更新/删除后事件驱动失效 + 乐观更新
支付结果、库存强制刷新 + 轮询/长连接
静态资源、构建产物ETag / 版本号

React Query 示例:

tsx
useQuery({
  queryKey: ['orders'],
  queryFn: fetchOrders,
  staleTime: 1000 * 60,
  gcTime: 1000 * 60 * 5,
});

// mutation 成功后失效
const queryClient = useQueryClient();
const mutation = useMutation({
  mutationFn: createOrder,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['orders'] });
  },
});

评分维度

  • 能列举至少 4 种缓存失效策略(40%)
  • 能说明不同策略的适用场景(30%)
  • 能结合 React Query / SWR 给出配置或代码示例(30%)

常见错误

  • 只使用 TTL,忽略事件驱动失效。
  • 对强一致性场景使用纯 SWR 策略。
  • 失效范围过大,导致无关 queryKey 被清空。

延伸追问

  • staleTimegcTime 有什么区别?
  • 如何设计一个既能离线使用又能及时失效的缓存方案?

相关题目

参考资源

口头回答版

前端缓存失效策略主要有这几种:TTL 固定过期时间;SWR 先返回缓存再后台刷新;事件驱动失效,就是 mutation 成功后手动让相关缓存失效;乐观更新,先改 UI 再发请求;还有手动刷新、基于版本号或 ETag 的缓存。选择看业务:展示类数据用 SWR,配置类用 TTL,写操作后用事件驱动失效,支付库存用强制刷新或轮询。


FB-29-CO-A-011:乐观更新是什么?适用场景和风险有哪些?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:乐观更新、乐观 UI、回滚、数据一致性、用户体验 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请解释乐观更新(Optimistic Update)的概念,说明其适用场景,并分析可能带来的风险与应对方案。

参考答案

乐观更新是一种 UI 更新策略:在用户触发写操作后,先假设操作会成功,立即更新界面,同时后台异步发送请求;如果请求失败,再将界面回滚到之前的状态。

典型流程:

text
用户点击点赞


立即更新 UI(点赞数 +1,按钮高亮)


后台发送请求

  ├─ 成功 ──▶ 保持 UI

  └─ 失败 ──▶ 回滚 UI,提示用户

适用场景:

  • 用户期望即时反馈的操作:点赞、收藏、删除、添加待办。
  • 网络延迟明显但失败率低的场景。
  • 操作结果可预测、回滚成本低。

风险与应对:

  1. 请求失败导致状态不一致
    • 必须实现回滚机制,保留更新前的快照。
  2. 并发冲突
    • 多个用户同时修改同一份数据,可能导致覆盖。
    • 应对:版本号、last-write-wins、合并策略。
  3. 后续读请求读到旧数据
    • 乐观更新本地状态后,服务端缓存或 CDN 仍可能是旧值。
    • 应对:更新后主动失效相关缓存或重新验证。
  4. 用户体验问题
    • 回滚动画、toast 提示要柔和,避免用户困惑。

React Query 乐观更新示例:

tsx
const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] });
    const previousTodo = queryClient.getQueryData(['todos', newTodo.id]);
    queryClient.setQueryData(['todos', newTodo.id], newTodo);
    return { previousTodo };
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos', newTodo.id], context.previousTodo);
  },
  onSettled: (newTodo) => {
    queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] });
  },
});

评分维度

  • 能解释乐观更新“先更新 UI 再发请求”的核心思想(40%)
  • 能说明适用场景(20%)
  • 能分析失败回滚、并发冲突等风险并给出应对方案(40%)

常见错误

  • 所有写操作都用乐观更新,忽略高失败率场景。
  • 没有实现回滚机制,失败后 UI 和服务端数据不一致。
  • 乐观更新后忘记失效或重新验证相关缓存。

延伸追问

  • 乐观更新和悲观更新各适合什么场景?
  • 如果用户在乐观更新后立刻跳转到其他页面,回滚逻辑如何处理?

相关题目

参考资源

口头回答版

乐观更新就是用户点了一个操作后,不等服务器返回,先把 UI 改了,假设会成功,后台再去发请求。成功就保持,失败就回滚。适合点赞、收藏、添加待办这种需要即时反馈、失败率低的操作。风险主要是请求失败后 UI 和服务端不一致,所以要做好回滚;还有并发冲突,可能需要版本号或合并策略。React Query 里用 onMutate 改缓存、onError 回滚、onSettled 重新验证。


FB-29-CO-A-012:MobX 和 Redux 的响应式模型有什么区别?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:MobX、Redux、响应式、不可变、面向对象 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请对比 MobX 和 Redux 在响应式模型、数据修改方式和使用体验上的差异。

参考答案

维度ReduxMobX
范式函数式,强调不可变数据面向对象/响应式,允许可变数据
数据流单向:dispatch -> reducer -> new state双向追踪:observable -> reaction -> autorun
修改方式必须通过 dispatch action,reducer 返回新 state在 action 中直接修改 observable 对象
订阅粒度通常整个 store 或切片细粒度,自动追踪使用到的属性
样板代码较多(action/reducer)较少
调试Redux DevTools 时间旅行MobX DevTools 依赖追踪
学习曲线较陡较平缓

Redux 模型:

ts
// 纯函数,返回新 state
function reducer(state = 0, action) {
  if (action.type === 'inc') return state + 1;
  return state;
}
store.dispatch({ type: 'inc' });

MobX 模型:

ts
import { makeAutoObservable, autorun } from 'mobx';

class Counter {
  count = 0;
  constructor() {
    makeAutoObservable(this);
  }
  increment() {
    this.count++; // 直接修改
  }
}

const counter = new Counter();
autorun(() => console.log(counter.count));
counter.increment();

核心区别:

  • Redux 通过不可变更新和纯函数保证可预测性。
  • MobX 通过 Proxy/observable 自动追踪依赖,修改更自然,但需要对响应式原理有理解。

选型建议:

  • 需要严格数据流、团队协作规范、时间旅行调试:Redux Toolkit。
  • 希望代码简洁、类 OOP 风格、自动追踪依赖:MobX。

评分维度

  • 能从不可变 vs 可变、函数式 vs 响应式对比(40%)
  • 能说明订阅粒度差异(30%)
  • 能给出选型建议(30%)

常见错误

  • 在 MobX 中手动返回新对象,违背其设计。
  • 在 Redux 中直接修改 state。
  • 认为 MobX 不适合大型应用(实际有 mobx-state-tree 等方案)。

延伸追问

  • MobX 的 makeAutoObservablemakeObservable 有什么区别?
  • MobX 如何处理异步 action?

相关题目

参考资源

口头回答版

Redux 是函数式的,强调不可变数据,所有修改都要 dispatch action,reducer 返回新 state。MobX 是响应式的,用 observable 自动追踪依赖,可以直接修改数据。Redux 适合需要严格数据流和时间旅行调试的大型项目;MobX 写起来更自然,代码量少,适合喜欢面向对象风格的团队。两者没有谁绝对好,看团队习惯和项目复杂度。


FB-29-CO-A-013:Jotai 和 Zustand 的设计理念有什么不同?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理、15 React 标签:Jotai、Zustand、原子化状态、状态管理、React 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请对比 Jotai 和 Zustand 的设计理念、API 风格及适用场景。

参考答案

Jotai 和 Zustand 都是轻量级 React 状态管理库,但设计哲学不同:

维度ZustandJotai
模型单一外部 Store,类似 Redux原子化(Atoms),类似 Recoil
状态组织一个 store 内包含多个字段每个 atom 是一个独立状态单元
派生状态用 selector 或单独 store用 atom 组合 / atom 计算属性
订阅粒度selector 按需订阅自动按 atom 订阅
API 风格命令式 set/get声明式 useAtom
跨组件共享全局 store原子可在模块间导出共享
适用场景全局/局部状态、简单到中等复杂度需要细粒度、组合式状态,复杂派生

Zustand 示例:

ts
const useStore = create((set) => ({
  count: 0,
  inc: () => set((s) => ({ count: s.count + 1 })),
}));

const count = useStore((s) => s.count);

Jotai 示例:

ts
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);
const doubleAtom = atom((get) => get(countAtom) * 2);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  const [double] = useAtom(doubleAtom);
  return <button onClick={() => setCount((c) => c + 1)}>{count} / {double}</button>;
}

选型建议:

  • 状态结构扁平、以全局 store 为主:Zustand。
  • 状态之间存在大量派生关系、希望细粒度订阅:Jotai。
  • 两者可以结合使用:Zustand 管全局,Jotai 管组件级原子状态。

评分维度

  • 能说明 Zustand 的单一 Store 与 Jotai 的原子化模型差异(40%)
  • 能对比订阅粒度和派生状态实现(30%)
  • 能给出选型建议(30%)

常见错误

  • 认为 Jotai 只能用于局部状态(atom 可全局导出)。
  • 在 Zustand 中每次返回新对象导致 selector 失效。
  • 把 Recoil、Jotai、Zustand 混为一谈。

延伸追问

  • Jotai 的 atom 和 Recoil 的 atom 有什么区别?
  • Zustand 的 selector 如何避免不必要的重渲染?

相关题目

参考资源

口头回答版

Zustand 是单一 Store 模型,像 Redux 但更简单,用 selector 按需订阅;Jotai 是原子化模型,每个状态是一个 atom,可以互相组合派生,订阅粒度更细。Zustand 适合全局状态或简单场景,Jotai 适合状态之间有很多派生关系、需要细粒度订阅的场景。两者也能一起用。


FB-29-CO-A-014:React Hook Form 和 Formik 有什么区别?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理、15 React 标签:React Hook Form、Formik、表单、非受控、性能 出现频率:中频 预计回答时长:3-5 分钟

题目描述: 请对比 React Hook Form 和 Formik,说明它们的核心差异和适用场景。

参考答案

维度FormikReact Hook Form
控制方式受控表单,内部管理 state非受控表单,基于 ref 和 DOM
性能字段多时易重渲染字段多也稳定,重渲染少
API 复杂度较复杂,组件较多简洁,基于 hooks
包体积较大较小
校验支持 Yup / Zod / 自定义原生支持 resolver,可集成 Zod/Yup
TypeScript支持一般原生优秀
学习曲线中等
适用场景中小型表单、团队熟悉受控大型表单、性能敏感

Formik 示例:

tsx
import { Formik, Form, Field } from 'formik';

<Formik
  initialValues=&#123;&#123; email: '' &#125;&#125;
  onSubmit={(values) => console.log(values)}
>
  <Form>
    <Field name="email" type="email" />
    <button type="submit">Submit</button>
  </Form>
</Formik>

React Hook Form 示例:

tsx
import { useForm } from 'react-hook-form';

function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      <button type="submit">Submit</button>
    </form>
  );
}

选型建议:

  • 字段多、性能要求高、希望代码简洁:React Hook Form。
  • 团队熟悉受控表单、需要大量内置组件封装:Formik。
  • 新项目一般优先 React Hook Form。

评分维度

  • 能说出受控 vs 非受控的核心差异(40%)
  • 能对比性能、体积、学习曲线(30%)
  • 能给出选型建议(30%)

常见错误

  • 认为 React Hook Form 不能做受控组件(实际支持 Controller)。
  • 在 React Hook Form 中仍然为每个字段写 useState。
  • 忽略校验库的集成方式差异。

延伸追问

  • React Hook Form 的 registerController 有什么区别?
  • 如何处理表单字段之间的联动?

相关题目

参考资源

口头回答版

Formik 是受控表单,内部自己管 state,API 相对复杂,字段多了容易渲染多;React Hook Form 是非受控的,基于 ref 和 DOM,性能更好,包也更小,API 简洁。新项目或大型表单一般优先 React Hook Form。两者都能集成 Yup、Zod 做校验。


FB-29-CO-A-015:数据一致性在前端有哪些常见问题?如何处理?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:数据一致性、缓存一致性、并发、乐观更新、版本号 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请分析前端数据一致性的常见问题,并给出对应的处理策略。

参考答案

前端数据一致性问题主要来源于缓存、并发和异步更新:

  1. 缓存与源数据不一致

    • 原因:前端缓存了旧数据,服务端数据已变化。
    • 处理:SWR 重新验证、事件驱动失效、TTL 过期、主动刷新。
  2. 多个组件持有同一数据的不同副本

    • 原因:同一 API 数据被多处存储在各自的 state 或 store。
    • 处理:使用单一数据源(Single Source of Truth),如 React Query 的共享缓存。
  3. 乐观更新失败导致 UI 与服务端不一致

    • 原因:先更新 UI 但请求失败未回滚。
    • 处理:保存快照、实现回滚机制、失败提示。
  4. 并发写冲突

    • 原因:多个用户或同一用户多个 tab 同时修改同一份数据。
    • 处理:版本号 / ETag、last-write-wins、 operational transformation / CRDT(协作场景)。
  5. mutation 与 query 的顺序问题

    • 原因:写操作后读到的仍是缓存旧值。
    • 处理:mutation 成功后 invalidate 相关 query,或 optimistic update 后立即 revalidate。
  6. 离线后恢复导致冲突

    • 原因:离线期间本地修改与服务端最新数据冲突。
    • 处理:本地队列、冲突检测、合并策略、用户确认。

处理策略总结:

问题策略
缓存旧数据SWR、失效、TTL、主动刷新
多处副本单一数据源、服务端状态库
乐观更新失败快照回滚
并发冲突版本号、ETag、CRDT
读写顺序invalidateQueries、refetch
离线冲突本地队列 + 冲突解决

评分维度

  • 能列举至少 4 种一致性常见问题(40%)
  • 能针对每种问题给出处理策略(40%)
  • 能说明单一数据源、版本号等核心思想(20%)

常见错误

  • 只关注写操作,忽略读缓存的一致性。
  • 对所有场景都使用乐观更新。
  • 没有版本号机制就处理并发写。

延伸追问

  • 如果两个 tab 同时修改同一个购物车,如何保证一致性?
  • 在设计 API 时,哪些字段有助于前端保持一致性?

相关题目

参考资源

口头回答版

前端数据一致性问题主要有几种:缓存里的旧数据和服务端不一致;多个组件各自保存同一份数据;乐观更新失败后没回滚;并发写冲突;mutation 后读到旧缓存;离线后恢复冲突。处理方式包括:用 SWR 或主动失效让缓存刷新;用单一数据源避免多处副本;乐观更新保存快照好回滚;并发写用版本号或 ETag;离线用本地队列加冲突解决。


FB-29-CO-A-016:状态提升(Lifting State Up)和状态下沉(State Colocation)各有什么适用场景?

题型:概念题 难度:🟡 进阶 岗位层级:高级 面试知识域:29 数据与状态管理 标签:状态提升、状态下沉、状态作用域、组件通信 出现频率:中频 预计回答时长:3-5 分钟

题目描述: 请解释状态提升和状态下沉的概念,并说明各自的适用场景和优缺点。

参考答案

  • 状态提升(Lifting State Up):把多个子组件共享的状态放到它们最近的公共父组件中管理,通过 props 向下传递。
  • 状态下沉(State Colocation):把状态尽量放在离使用它最近的组件,减少不必要的数据传递和影响范围。

适用场景:

场景推荐方式
两个 sibling 组件需要同步同一数据状态提升
状态只在一个组件及其子树中使用状态下沉
父组件过度渲染状态下沉
需要跨多层级共享Context / 全局 Store

示例(状态提升):

tsx
function Parent() {
  const [value, setValue] = useState('');
  return (
    <>
      <Input value={value} onChange={setValue} />
      <Preview value={value} />
    </>
  );
}

示例(状态下沉):

tsx
function Parent() {
  return <Form />; // Form 内部自己管理自己的 state
}

function Form() {
  const [value, setValue] = useState('');
  return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}

优缺点:

  • 状态提升:
    • 优点:共享数据一致,便于同步。
    • 缺点:父组件可能变得臃肿,props drilling。
  • 状态下沉:
    • 优点:组件独立、可复用、渲染范围小。
    • 缺点:需要共享时又得提升。

最佳实践:

  • 先用状态下沉,需要共享时再提升。
  • 提升后若 props drilling 严重,考虑 Context 或状态管理库。

评分维度

  • 能解释状态提升和状态下沉的含义(40%)
  • 能给出不同场景下的选择依据(30%)
  • 能分析两者的优缺点(30%)

常见错误

  • 一开始就过度提升,导致父组件复杂。
  • 该提升时不提升,导致兄弟组件数据不同步。
  • 用全局 Store 解决本可以用状态提升解决的问题。

延伸追问

  • 状态提升和 Context 是什么关系?
  • 如何判断一个状态应该放在局部还是全局?

相关题目

参考资源

口头回答版

状态提升是把共享状态放到最近的公共父组件,让兄弟组件都能拿到;状态下沉是把状态放到离使用最近的组件,减少影响范围。一般先用下沉,需要共享再提升。提升太多会让父组件臃肿、props 一层层传,这时候可以考虑 Context 或状态管理库。


深入题(8 道)

FB-29-FS-P-017:Redux 中间件原理是什么?请手写一个 logger 中间件

题型:框架原理题 / 手写代码题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理、15 React 标签:Redux、中间件、logger、函数式编程、compose 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请解释 Redux 中间件的工作原理,并手写一个 logger 中间件,要求能打印 action 和 state 变化前后的值。

参考答案

Redux 中间件是一个柯里化函数,形式为:

ts
const middleware = (store) => (next) => (action) => {
  // 在 dispatch 前执行
  const result = next(action);
  // 在 dispatch 后执行
  return result;
};

中间件原理:

  1. Redux 的 applyMiddleware 会改造 store.dispatch
  2. 每个中间件拿到 store 的引用和 next(下一个中间件或原始 dispatch)。
  3. 中间件可以在 action 到达 reducer 前后执行副作用,如日志、异步请求、崩溃上报等。
  4. 多个中间件通过 compose 组合成洋葱模型。

手写 logger 中间件:

ts
const loggerMiddleware = (store) => (next) => (action) => {
  console.group(action.type);
  console.log('prev state', store.getState());
  console.log('action', action);

  const result = next(action);

  console.log('next state', store.getState());
  console.groupEnd();

  return result;
};

完整使用示例:

ts
import { createStore, applyMiddleware } from 'redux';

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

const store = createStore(counterReducer, applyMiddleware(loggerMiddleware));

store.dispatch({ type: 'INCREMENT' });

执行顺序:

text
dispatch(action)


logger 前:打印 prev state / action


next(action) -> 下一个中间件或 reducer


logger 后:打印 next state

如果中间件需要处理异步,常见形式如 redux-thunk:

ts
const thunkMiddleware = (store) => (next) => (action) => {
  if (typeof action === 'function') {
    return action(store.dispatch, store.getState);
  }
  return next(action);
};

评分维度

  • 能准确解释中间件柯里化形式和洋葱模型(40%)
  • 能正确手写 logger 中间件(40%)
  • 能说明中间件在 dispatch 前后的执行时机(20%)

常见错误

  • 忘记调用 next(action),导致 action 无法到达 reducer。
  • 在 logger 中直接修改 action 或 state。
  • 混淆中间件和 reducer 的职责。

延伸追问

  • redux-thunk 和 redux-saga 各有什么优缺点?
  • Redux Toolkit 的 createAsyncThunk 如何简化异步流程?

相关题目

参考资源

口头回答版

Redux 中间件是一个三层柯里化函数:store => next => action => {}。它可以在 action 到达 reducer 前后做事情。applyMiddleware 会把多个中间件串起来,每个中间件调用 next(action) 把动作传给下一个。logger 中间件就是在 next 之前打印 prev state 和 action,next 之后打印 next state。一定要记得调用 next,否则 action 到不了 reducer。


FB-29-FS-P-018:React Query 的缓存和失效机制底层是怎么工作的?

题型:框架原理题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理、15 React 标签:React Query、TanStack Query、缓存、queryKey、失效、垃圾回收 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请深入说明 React Query 的缓存结构、queryKey 作用、失效触发条件和垃圾回收机制。

参考答案

React Query 内部维护一个 QueryCache,每个查询对应一个 Query 实例,由 queryKey 唯一标识。

核心机制:

  1. queryKey 作为缓存键

    • queryKey 经过序列化后作为 Query 的唯一 ID。
    • 相同 queryKey 的组件共享同一个 Query 实例和结果。
    • queryKey 通常包含资源类型和 ID,如 ['user', userId]
  2. Query 状态生命周期

    • fresh:数据在 staleTime 内,认为是最新的,不会重新请求。
    • stale:数据超过 staleTime,但仍在 gcTime 内,可以被立即返回并后台重新验证。
    • inactive:没有任何组件订阅该 Query。
    • 超过 gcTime 后,Query 被垃圾回收。
  3. 失效触发条件

    • staleTime 过期。
    • 窗口重新聚焦(默认 refetchOnWindowFocus: true)。
    • 网络恢复(默认 refetchOnReconnect: true)。
    • 手动调用 queryClient.invalidateQueries({ queryKey: [...] })
    • 新订阅者挂载且数据是 stale 的。
  4. Mutation 与缓存失效

    • mutation 成功后,通过 invalidateQueries 使相关 Query 标记为 stale,触发重新获取。
    • 也可以通过 setQueryData 直接更新缓存,实现乐观更新或即时同步。
  5. 垃圾回收(gcTime)

    • 默认 5 分钟。
    • 当 Query 没有任何 observer 且超过 gcTime 后,从 QueryCache 中移除。

代码示例:

tsx
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60, // 1 分钟内 fresh
      gcTime: 1000 * 60 * 5, // 5 分钟后回收
      refetchOnWindowFocus: true,
    },
  },
});

// 失效订单列表
queryClient.invalidateQueries({ queryKey: ['orders'] });

评分维度

  • 能说明 queryKey 作为缓存键的原理(30%)
  • 能解释 fresh/stale/inactive/gc 生命周期(30%)
  • 能列举失效触发条件和 mutation 失效机制(25%)
  • 能说明 gcTime 垃圾回收机制(15%)

常见错误

  • 认为 staleTime 是缓存过期时间(实际是“新鲜”时间)。
  • 混淆 staleTimegcTime
  • queryKey 设计不稳定,导致缓存命中率低。

延伸追问

  • 如果两个组件的 queryKey 完全一样但 queryFn 不同,会发生什么?
  • 如何设计 queryKey 才能兼顾缓存命中和失效精度?

相关题目

参考资源

口头回答版

React Query 内部有一个 QueryCache,每个 queryKey 对应一个 Query 实例,相同 queryKey 的组件共享结果。Query 有生命周期:fresh 是还在 staleTime 内,不会重新请求;stale 是过了 staleTime,可以返回缓存同时后台刷新;inactive 是没人订阅;超过 gcTime 就被回收。失效可以由 staleTime 过期、窗口聚焦、网络恢复、手动 invalidateQueries 触发。mutation 成功后一般用 invalidateQueries 让相关缓存失效,或用 setQueryData 直接更新。


FB-29-CO-P-019:状态机在前端状态管理中的应用是什么?XState 的核心概念有哪些?

题型:概念题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理 标签:状态机、XState、Finite State Machine、状态转换、副作用 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明有限状态机(FSM)在前端状态管理中的价值,并介绍 XState 的核心概念。

参考答案

有限状态机(FSM)是一种数学模型,包含有限个状态、状态之间通过事件触发转换。在前端状态管理中,FSM 的价值在于:

  1. 消除非法状态:通过显式定义状态和转换,避免“不可能状态”组合,如 isLoading && isError
  2. 可视化状态流转:状态图清晰表达业务逻辑,便于沟通和维护。
  3. 集中管理副作用:进入某个状态时自动执行 action、invoke 服务等。
  4. 可预测:给定状态和事件,下一个状态是确定的。

XState 核心概念:

  • Machine:状态机定义,包含 states、events、transitions。
  • State:当前状态节点,包括 value 和 context(扩展状态)。
  • Event:触发状态转换的信号。
  • Transition:状态之间的转换规则,on: { EVENT: 'targetState' }
  • Context:存储额外数据,可变但非状态本身。
  • Action:进入/离开状态时执行的副作用,如发送日志、调用函数。
  • Service:运行状态机的实例,可以发送事件、读取当前状态。

示例:

ts
import { createMachine, interpret } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } },
  },
});

const service = interpret(toggleMachine).start();
service.send('TOGGLE');

适用场景:

  • 复杂表单流程、订单状态流转、支付流程、权限审批、多人协作状态。

评分维度

  • 能说明 FSM 消除非法状态、可视化流转的价值(40%)
  • 能说出 XState 的 Machine、State、Event、Context、Action(40%)
  • 能给出适用场景(20%)

常见错误

  • 把所有状态都用 FSM 管理,导致过度设计。
  • 把业务数据放在 state value 中而非 context。
  • 忽略 XState 的学习和迁移成本。

延伸追问

  • XState 如何处理嵌套状态和并行状态?
  • 状态机和 Redux reducer 有什么联系?

相关题目

参考资源

口头回答版

状态机能让我们显式定义所有状态和转换,避免出现不可能的状态组合,比如同时 loading 和 error。XState 是前端常用的状态机库,核心概念有 Machine、State、Event、Transition、Context、Action。Machine 是定义,State 是当前状态,Event 触发转换,Context 存额外数据,Action 做副作用。适合复杂流程,比如订单状态、支付流程、表单审批。


FB-29-CO-P-020:乐观更新和悲观更新如何选择?如何处理失败回滚?

题型:概念题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理 标签:乐观更新、悲观更新、回滚、数据一致性、错误处理 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请对比乐观更新和悲观更新,说明选择策略,并详细说明失败回滚的实现方式。

参考答案

维度悲观更新(Pessimistic)乐观更新(Optimistic)
更新时机请求成功后更新 UI请求前更新 UI
用户体验有等待感,可能需要 loading即时反馈
一致性风险高,需回滚机制
实现复杂度
适用场景支付、删除、强一致性要求点赞、收藏、待办、评论

选择策略:

  • 用乐观更新:操作结果可预测、失败率低、回滚成本低、用户期望即时反馈。
  • 用悲观更新:失败率高、后果严重、需要服务端确认、强一致性要求。
  • 混合使用:关键字段悲观,非关键字段乐观。

失败回滚实现方式:

  1. 快照回滚

    • 更新前保存旧状态快照。
    • 请求失败后将状态恢复为快照。
  2. React Query 回滚

    • onMutate 中保存 previousData
    • onError 中用 setQueryData 恢复。
  3. 状态机回滚

    • 设计 submitting -> success / error 状态,error 时回退到编辑态。
  4. 用户确认回滚

    • 冲突严重时提示用户选择保留本地还是服务端版本。

示例(React Query 回滚):

tsx
const mutation = useMutation({
  mutationFn: likePost,
  onMutate: async (postId) => {
    await queryClient.cancelQueries({ queryKey: ['post', postId] });
    const previousPost = queryClient.getQueryData(['post', postId]);
    queryClient.setQueryData(['post', postId], (old) => ({ ...old, liked: true }));
    return { previousPost };
  },
  onError: (err, postId, context) => {
    queryClient.setQueryData(['post', postId], context.previousPost);
  },
  onSettled: (postId) => {
    queryClient.invalidateQueries({ queryKey: ['post', postId] });
  },
});

评分维度

  • 能清晰对比乐观更新和悲观更新(40%)
  • 能给出选择策略和典型场景(30%)
  • 能详细说明快照回滚、React Query 回滚等实现(30%)

常见错误

  • 对高风险操作使用乐观更新。
  • 只更新 UI 不保存快照,失败后无法恢复。
  • 回滚时不提示用户,导致用户困惑。

延伸追问

  • 如果乐观更新后用户立即离开页面,回滚怎么办?
  • 如何设计一个既能乐观更新又能保证最终一致性的系统?

相关题目

参考资源

口头回答版

悲观更新是等服务器返回成功后再改 UI,安全但慢;乐观更新是先改 UI 再发请求,快但需要回滚。点赞、收藏、待办适合乐观;支付、删除、库存扣减适合悲观。回滚的关键是更新前保存旧状态快照,失败时恢复。React Query 里用 onMutate 保存 previousData,onError 时 setQueryData 恢复,onSettled 再重新验证。


FB-29-CO-P-021:离线同步和数据冲突解决有哪些策略?

题型:概念题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理 标签:离线同步、数据冲突、本地队列、CRDT、最终一致性 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明前端离线同步的常见方案,以及当本地数据和服务端数据冲突时有哪些解决策略。

参考答案

离线同步常见方案:

  1. 本地缓存 + 联网刷新

    • 离线时展示缓存数据,联网后重新请求最新数据。
    • 适合只读或低频写场景。
  2. 本地队列(Operation Queue)

    • 离线期间的操作按顺序存入本地队列(如 IndexedDB)。
    • 联网后按 FIFO 或自定义顺序依次同步到服务端。
  3. 乐观本地更新 + 冲突检测

    • 本地立即生效,联网后和服务端版本对比。
    • 若冲突,按策略合并或提示用户。
  4. CRDT(Conflict-free Replicated Data Types)

    • 数学上保证合并结果一致,无需中心协调。
    • 适合协作文档、多人编辑等场景,如 Yjs、Automerge。
  5. Operational Transformation(OT)

    • 将操作转换后再应用,保证多方操作顺序一致。
    • 传统在线文档协作方案,如 Google Docs。

冲突解决策略:

策略说明适用场景
Last-Write-Wins(LWW)以最后修改时间为准简单计数、配置
版本号 / ETag服务端比较版本,冲突时拒绝单字段更新
合并(Merge)自动合并非冲突字段对象级更新
用户确认冲突时弹窗让用户选择重要内容编辑
CRDT / OT自动保持最终一致协作文档

注意事项:

  • 明确最终一致性还是强一致性。
  • 离线队列要有重试、去重、失败处理。
  • 冲突解决策略要在产品层明确,避免技术黑盒。

评分维度

  • 能列举至少 3 种离线同步方案(40%)
  • 能说明至少 3 种冲突解决策略(40%)
  • 能指出重试、去重、一致性等注意事项(20%)

常见错误

  • 离线写操作直接丢弃,不保存队列。
  • 所有冲突都用 Last-Write-Wins,忽略业务语义。
  • 不考虑离线队列的幂等性。

延伸追问

  • 如何保证离线队列的幂等性?
  • 设计一个离线购物车,如何处理多端同步?

相关题目

参考资源

口头回答版

离线同步常见有几种:只读场景用本地缓存加联网刷新;写操作多就用本地队列,联网后按顺序同步;复杂协作用 CRDT 或 OT。冲突解决策略包括:最后写入为准、版本号拒绝冲突、自动合并、用户确认。离线队列要注意重试、去重和幂等性,不能简单丢弃,也不能所有冲突都自动覆盖。


FB-29-CD-P-022:如何手写一个简化的状态管理库(类似 Zustand)?

题型:手写代码题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理、15 React 标签:手写代码、状态管理、Zustand、发布订阅、React Hook 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请手写一个简化的全局状态管理库,要求支持:创建 store、读取/更新状态、订阅变化、在 React 组件中通过 Hook 使用。

参考答案

核心思路:

  1. 用一个对象保存全局 state。
  2. 提供 set 方法更新 state。
  3. 维护订阅者列表,state 变化时通知所有订阅者。
  4. 提供 React Hook,订阅 state 变化并按需重渲染。

实现:

ts
// store.ts
export const createStore = (initialState) => {
  let state = initialState;
  const listeners = new Set();

  const getState = () => state;

  const setState = (partial) => {
    const nextState = typeof partial === 'function' ? partial(state) : partial;
    state = { ...state, ...nextState };
    listeners.forEach((listener) => listener(state));
  };

  const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };

  return { getState, setState, subscribe };
};

React Hook:

tsx
// useStore.ts
import { useEffect, useState, useSyncExternalStore } from 'react';

export const createUseStore = (store) => (selector = (state) => state) => {
  // React 18 推荐方式
  return useSyncExternalStore(
    store.subscribe,
    () => selector(store.getState()),
    () => selector(store.getState())
  );

  // 兼容 React 17 的方式
  // const [state, setState] = useState(() => selector(store.getState()));
  // useEffect(() => store.subscribe((newState) => setState(selector(newState))), []);
  // return state;
};

使用:

tsx
const store = createStore({ count: 0, name: 'Tom' });
const useStore = createUseStore(store);

function Counter() {
  const count = useStore((state) => state.count);
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => store.setState((s) => ({ count: s.count + 1 }))}>
        +
      </button>
    </div>
  );
}

关键点:

  • 使用 useSyncExternalStore 保证并发安全(React 18)。
  • selector 可以让组件只订阅需要的字段,减少重渲染。
  • setState 支持函数式更新,避免闭包问题。

评分维度

  • 能实现 createStore 的 getState / setState / subscribe(40%)
  • 能实现 React Hook 订阅和 selector(30%)
  • 能提到 useSyncExternalStore 和并发安全(20%)
  • 能说明 selector 减少重渲染的作用(10%)

常见错误

  • 订阅时每次更新都触发所有组件重渲染,不做 selector。
  • 在 useEffect 中订阅但没有正确清理。
  • setState 直接修改原 state,不使用不可变更新。

延伸追问

  • 如何在这个基础上实现中间件?
  • 如何让 store 支持异步 action?

相关题目

参考资源

口头回答版

手写一个类似 Zustand 的状态库,核心是三个方法:getState 读状态,setState 更新并通知订阅者,subscribe 注册监听。state 变化时遍历 listeners 调用。React Hook 里用 useSyncExternalStore 订阅 store,再传入 selector 让组件只监听需要的字段。setState 要支持函数式更新,避免闭包问题。


FB-29-CO-P-023:Event Sourcing 和 CQRS 在前端如何应用?

题型:概念题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理 标签:Event Sourcing、CQRS、事件溯源、命令查询分离、前端架构 出现频率:低频 预计回答时长:8-15 分钟

题目描述: 请解释 Event Sourcing 和 CQRS 的概念,并说明它们在前端状态管理中的应用场景和实现思路。

参考答案

CQRS(Command Query Responsibility Segregation)

将“写操作”(Command)和“读操作”(Query)分离,使用不同的模型处理。前端常见体现:

  • :使用 React Query / SWR 从服务端读取数据,本地只做展示。
  • :通过 mutation / action 发送命令到服务端,成功后失效读缓存。

前端 CQRS 示例:

tsx
// Query:读取订单列表
const { data: orders } = useQuery({ queryKey: ['orders'], queryFn: fetchOrders });

// Command:创建订单
const mutation = useMutation({
  mutationFn: createOrder,
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['orders'] }),
});

Event Sourcing(事件溯源)

不保存最终状态,而是保存所有改变状态的事件序列。通过重放事件得到任意时刻的状态。

前端应用场景:

  1. Undo/Redo:保存用户操作事件,支持撤销重做。
  2. 审计日志:记录用户每一步操作。
  3. 协作编辑:操作序列化后同步给其他客户端。
  4. 时间旅行调试:Redux DevTools 就是事件溯源思想的体现。

前端实现思路:

ts
type Event =
  | { type: 'ADD_TODO'; payload: { id: string; text: string } }
  | { type: 'TOGGLE_TODO'; payload: { id: string } };

const events: Event[] = [];

const applyEvent = (state, event) => {
  switch (event.type) {
    case 'ADD_TODO':
      return [...state, event.payload];
    case 'TOGGLE_TODO':
      return state.map((todo) =>
        todo.id === event.payload.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
};

// 重放得到当前状态
const currentState = events.reduce(applyEvent, []);

注意事项:

  • Event Sourcing 会增加存储和复杂度,不是所有应用都需要。
  • CQRS 读模型和写模型不一致时,要处理最终一致性。
  • 前端通常只做轻量级事件溯源,核心事件存储在服务端。

评分维度

  • 能解释 CQRS 的读写分离思想(30%)
  • 能解释 Event Sourcing 的事件重放思想(30%)
  • 能给出前端应用场景和实现思路(30%)
  • 能说明复杂度和注意事项(10%)

常见错误

  • 把 CQRS 和 Event Sourcing 混为一谈(两者可以独立使用)。
  • 在前端对所有状态都使用事件溯源,导致过度设计。
  • 忽略读模型和写模型的延迟一致性。

延伸追问

  • Redux 的 action log 和 Event Sourcing 有什么关系?
  • 如何在事件溯源中处理事件版本升级?

相关题目

参考资源

口头回答版

CQRS 是把读和写分开。前端常见做法是读用 React Query 拉数据,写用 mutation 发命令,成功后让读缓存失效。Event Sourcing 是不保存最终状态,而是保存所有事件,通过重放事件得到状态。前端适合做撤销重做、审计日志、协作编辑。Redux DevTools 的时间旅行也是这个思想。但不要在所有地方都用事件溯源,复杂度会很高。


FB-29-CO-P-024:Repository 模式和 Data Mapper 模式在前端数据层有什么区别?

题型:概念题 难度:🔴 深入 岗位层级:专家 面试知识域:29 数据与状态管理 标签:Repository、Data Mapper、数据层、抽象、Clean Architecture 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请对比 Repository 模式和 Data Mapper 模式在前端数据层抽象中的应用,并说明各自的优缺点。

参考答案

Repository 模式

将数据访问逻辑封装到 Repository 对象中,组件/业务层不直接调用 API,而是通过 Repository 接口获取数据。

特点:

  • 隐藏数据源细节(REST、GraphQL、IndexedDB、Mock)。
  • 返回领域模型或 DTO。
  • 便于切换数据源和单元测试。

示例:

ts
class UserRepository {
  async getById(id: string): Promise<User> {
    const dto = await api.get(`/users/${id}`);
    return User.fromDTO(dto);
  }

  async update(user: User): Promise<void> {
    await api.put(`/users/${user.id}`, user.toDTO());
  }
}

Data Mapper 模式

在 Repository 基础上增加一层 Mapper,专门负责原始数据(DTO)和领域模型之间的转换。

特点:

  • Repository 只负责数据获取和持久化。
  • Mapper 负责 DTO <-> Entity 转换。
  • 转换逻辑集中,便于维护和复用。

示例:

ts
class UserMapper {
  toDomain(dto: UserDTO): User {
    return new User(dto.id, dto.full_name);
  }

  toDTO(user: User): UserDTO {
    return { id: user.id, full_name: user.name };
  }
}

class UserRepository {
  private mapper = new UserMapper();

  async getById(id: string): Promise<User> {
    const dto = await api.get(`/users/${id}`);
    return this.mapper.toDomain(dto);
  }
}

对比:

维度RepositoryData Mapper
抽象层次封装数据访问额外封装数据转换
复杂度较低较高
适用场景中小型项目、转换简单大型项目、DTO 和领域模型差异大
测试性更好
与 Clean Architecture 关系位于数据层Repository + Mapper 位于数据层

前端应用建议:

  • 中小型项目:使用 Repository 模式即可。
  • 大型项目、字段映射复杂、多端数据格式不同:使用 Data Mapper。
  • 配合状态管理库时,Repository 可作为 action/mutation 的调用层。

评分维度

  • 能说明 Repository 封装数据访问的作用(30%)
  • 能说明 Data Mapper 负责 DTO 与领域模型转换(30%)
  • 能对比两者复杂度和适用场景(25%)
  • 能给出前端应用建议(15%)

常见错误

  • 在组件里直接调用 fetch/axios,没有数据层抽象。
  • 把转换逻辑分散在各个组件中。
  • 为了用模式而用模式,增加不必要的抽象。

延伸追问

  • 如果后端接口返回的字段命名和前端不一致,怎么处理?
  • Repository 模式和 React Query 的 queryFn 是什么关系?

相关题目

参考资源

口头回答版

Repository 模式是把数据访问封装起来,组件不直接调 API,而是通过 Repository。Data Mapper 是在 Repository 基础上再抽一层 Mapper,专门做 DTO 和领域模型之间的转换。Repository 适合中小项目;Data Mapper 适合大型项目或前后端字段差异大的场景。好处是数据源变了、字段映射变了,都只改一层,业务代码不动。


架构题(55 道)

FB-29-SD-R-025:如何设计一个前端数据层抽象层,支持多数据源切换和缓存?

题型:系统设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:数据层抽象、Repository、多数据源、缓存、Clean Architecture 出现频率:中频 预计回答时长:15-30 分钟

题目描述: 请设计一个前端数据层抽象层,要求支持 REST、GraphQL、本地 IndexedDB 等多种数据源切换,并内置统一的缓存和失效策略。

参考答案

设计目标:

  1. 业务层不感知底层数据源(REST/GraphQL/IndexedDB/Mock)。
  2. 统一缓存策略,支持内存缓存和持久化缓存。
  3. 统一的查询和写入语义。
  4. 便于单元测试和后续扩展。

分层架构:

text
UI / Components


Hooks / View Models  (useUser, useOrders)


Services / Use Cases (业务编排)


Repository Layer     (UserRepository, OrderRepository)


Data Sources         (RestDataSource, GraphQLDataSource, IndexedDBDataSource)


Cache Layer          (MemoryCache, IndexedDBCache)


Network / Storage    (fetch / GraphQL client / IndexedDB)

核心接口设计:

ts
interface DataSource<T> {
  query(key: QueryKey, options?: QueryOptions): Promise<T>;
  mutate(key: QueryKey, payload: unknown, options?: MutateOptions): Promise<T>;
}

interface Repository<T> {
  getById(id: string): Promise<T>;
  list(params?: Record<string, unknown>): Promise<T[]>;
  create(data: Partial<T>): Promise<T>;
  update(id: string, data: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

interface Cache {
  get<T>(key: string): Promise<T | undefined>;
  set<T>(key: string, value: T, ttl?: number): Promise<void>;
  invalidate(key: string): Promise<void>;
}

Repository 实现:

ts
class UserRepository implements Repository<User> {
  constructor(
    private dataSource: DataSource<User>,
    private cache: Cache,
    private mapper: UserMapper
  ) {}

  async getById(id: string): Promise<User> {
    const cacheKey = `user:${id}`;
    const cached = await this.cache.get<User>(cacheKey);
    if (cached) return cached;

    const dto = await this.dataSource.query(['user', id]);
    const user = this.mapper.toDomain(dto);
    await this.cache.set(cacheKey, user, 60_000);
    return user;
  }

  async update(id: string, data: Partial<User>): Promise<User> {
    const dto = await this.dataSource.mutate(['user', id], this.mapper.toDTO(data));
    const user = this.mapper.toDomain(dto);
    await this.cache.set(`user:${id}`, user);
    return user;
  }
}

数据源切换:

ts
const restSource = new RestDataSource({ baseURL: '/api' });
const graphQLSource = new GraphQLDataSource({ client });
const localSource = new IndexedDBDataSource({ db });

// 按环境或功能切换
const userRepo = new UserRepository(
  import.meta.env.VITE_OFFLINE_MODE ? localSource : restSource,
  new HybridCache(),
  new UserMapper()
);

缓存策略:

  • 内存缓存:LRU,短期高频数据。
  • 持久化缓存:IndexedDB,离线使用。
  • 失效:TTL、事件驱动、手动 invalidate。

评分维度

  • 能画出清晰的分层架构(30%)
  • 能设计出 Repository / DataSource / Cache 等核心接口(30%)
  • 能说明多数据源切换机制(20%)
  • 能说明缓存和失效策略(20%)

常见错误

  • 数据层和业务层耦合,组件直接调 API。
  • 缓存层和数据源层没有明确边界。
  • 忽略离线场景和缓存一致性。

延伸追问

  • 如何处理不同数据源的返回格式差异?
  • 如果某个数据源挂掉,如何优雅降级?

相关题目

参考资源

口头回答版

设计前端数据层抽象,我会分几层:最上面是 UI 和 Hooks,下面是业务 Service,再下面是 Repository,Repository 不直接调网络,而是依赖 DataSource 接口。DataSource 可以有 REST、GraphQL、IndexedDB 多种实现。再下面加 Cache 层,支持内存和持久化缓存。这样数据源切换只改配置,业务代码不动。缓存策略用 TTL、事件驱动失效和手动 invalidate 结合。


FB-29-SD-R-026:如何为大型 SPA 设计状态管理架构?

题型:系统设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:大型 SPA、状态管理、架构设计、微前端、模块拆分 出现频率:高频 预计回答时长:15-30 分钟

题目描述: 请为大型单页应用设计一套状态管理架构,要求考虑状态分层、模块拆分、服务端状态、跨模块通信、微前端等场景。

参考答案

设计原则:

  1. 状态分层

    • UI 状态(局部):组件自己管理。
    • 全局客户端状态:主题、用户、权限、设置。
    • 服务端状态:API 数据,用 React Query / SWR 管理。
    • URL 状态:路由、筛选、分页。
  2. 模块拆分

    • 按业务域拆分 Store / Slice / Repository。
    • 每个模块独立,避免全局命名冲突。
  3. 单一数据源

    • 服务端状态不复制到多个地方。
    • 客户端全局状态避免重复存储。
  4. 按需加载

    • 大型 Store 按路由或模块懒加载。
  5. 跨模块通信

    • 优先通过 URL、全局事件总线、或最小共享状态通信。
    • 避免模块间直接依赖彼此的状态。

架构示意图:

text
┌─────────────────────────────────────────┐
│           UI Components                 │
├─────────────────────────────────────────┤
│  Local State (useState/useReducer)      │
├─────────────────────────────────────────┤
│  Server State (React Query / SWR)       │
├─────────────────────────────────────────┤
│  Global Client State (Zustand/Redux)    │
│  - user    - theme    - permission      │
├─────────────────────────────────────────┤
│  URL State (React Router / history)     │
├─────────────────────────────────────────┤
│  Data Layer (Repository + DataSource)   │
├─────────────────────────────────────────┤
│  API / GraphQL / LocalStorage           │
└─────────────────────────────────────────┘

微前端场景:

  • 每个微应用拥有自己的局部状态和 Repository。
  • 共享全局状态通过基座应用注入,或使用共享模块(如 Module Federation)。
  • 避免微应用之间直接依赖彼此的 Store。

示例(Redux Toolkit 切片):

ts
// features/user/userSlice.ts
import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: { profile: null, permissions: [] },
  reducers: {
    setUser(state, action) {
      state.profile = action.payload;
    },
  },
});

export const { setUser } = userSlice.actions;
export default userSlice.reducer;

最佳实践:

  • 服务端状态和客户端状态分离。
  • 全局状态最小化,优先局部化。
  • 状态变更要有明确来源,便于调试。
  • 使用 DevTools、持久化、日志中间件辅助维护。

评分维度

  • 能提出状态分层和模块拆分原则(30%)
  • 能区分服务端状态和客户端状态(25%)
  • 能说明跨模块通信和微前端场景(25%)
  • 能给出具体架构图或代码示例(20%)

常见错误

  • 所有状态都放全局 Store。
  • 模块间状态高度耦合。
  • 忽略服务端状态管理库,自己重复实现缓存。

延伸追问

  • 微前端之间如何安全共享状态?
  • 如果全局 Store 体积过大,如何优化首屏加载?

相关题目

参考资源

口头回答版

大型 SPA 状态管理要分层:UI 状态放组件;服务端状态用 React Query 或 SWR;全局客户端状态只放用户、主题、权限这种真正全局的;URL 状态交给路由。按业务域拆分 Store 或 Slice,避免命名冲突。微前端场景下,每个微应用有自己的状态,共享状态通过基座注入。核心原则是全局状态最小化、单一数据源、状态变更来源清晰。


FB-29-SC-R-027:设计一个高并发的购物车状态管理方案

题型:场景设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:购物车、高并发、状态管理、乐观更新、库存、一致性 出现频率:中频 预计回答时长:15-30 分钟

题目描述: 请设计一个电商购物车的状态管理方案,要求支持高并发场景(如秒杀),并处理库存校验、价格变动、多端同步等问题。

参考答案

核心挑战:

  1. 用户频繁加购、修改数量。
  2. 库存实时变化,需要防止超卖。
  3. 价格、促销可能变动。
  4. 多端同步(App、小程序、Web)。
  5. 高并发下秒杀场景库存扣减。

状态分层:

text
购物车 UI State(本地)


购物车 Client State(全局 Store)


购物车 Server State(服务端为真相源)

设计方案:

  1. 本地即时反馈

    • 用户点击“加购”或修改数量,立即更新本地购物车 UI。
    • 使用乐观更新,失败时回滚并提示。
  2. 服务端真相源

    • 购物车最终数据以服务端为准。
    • 每次进入购物车或结算时,拉取最新购物车快照。
  3. 库存校验

    • 加购时本地不真正校验库存,只做轻量提示。
    • 结算时服务端强制校验库存和价格,超卖直接拒绝。
    • 秒杀场景采用预扣库存 + 订单锁定机制。
  4. 价格与促销

    • 购物车展示的价格为“参考价”。
    • 结算时重新计算价格和优惠,避免前端价格被篡改。
  5. 多端同步

    • 服务端购物车数据通过 WebSocket / 轮询同步到各端。
    • 或采用事件驱动:加购成功后触发刷新。
  6. 离线支持

    • 本地缓存购物车,联网后合并到服务端。
    • 合并时处理冲突:以服务端为准,提示用户本地变化。

代码示例(加购流程):

ts
async function addToCart(skuId: string, quantity: number) {
  // 1. 乐观更新本地状态
  cartStore.optimisticallyAdd({ skuId, quantity });

  try {
    // 2. 请求服务端
    const result = await cartApi.add({ skuId, quantity });

    // 3. 同步服务端返回的精确数据
    cartStore.replaceWithServer(result);
  } catch (error) {
    // 4. 失败回滚
    cartStore.rollback(skuId);
    toast.error('加购失败,库存不足');
  }
}

秒杀场景:

  • 前端按钮防重复点击。
  • 服务端采用 Redis 原子扣减库存。
  • 扣减成功才写入购物车或生成订单。
  • 前端展示“抢购中”状态,避免用户频繁操作。

评分维度

  • 能识别库存、价格、多端同步等核心挑战(30%)
  • 能设计本地乐观更新 + 服务端真相源方案(30%)
  • 能说明秒杀场景的特殊处理(20%)
  • 能提到回滚、冲突处理、离线支持(20%)

常见错误

  • 前端做最终库存校验,导致超卖。
  • 购物车状态完全本地,不与服务端同步。
  • 忽略价格变动和促销失效。

延伸追问

  • 如果用户把商品加入购物车后长期不结算,价格变了怎么处理?
  • 多个 tab 同时操作购物车,如何保证一致性?

相关题目

参考资源

口头回答版

购物车状态管理要分本地和服务端两层。用户操作时先本地乐观更新,让 UI 及时响应,再以服务端为真相源,失败就回滚。库存校验不能靠前端,结算时服务端强制校验,秒杀用预扣库存。价格是参考价,结算时重新算。多端同步可以用 WebSocket 或事件驱动刷新。还要考虑离线缓存和冲突合并。


FB-29-SC-R-028:如何设计多端状态同步方案(Web、小程序、App)?

题型:场景设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:多端同步、Web、小程序、App、状态同步、冲突解决 出现频率:中频 预计回答时长:15-30 分钟

题目描述: 请设计一个多端状态同步方案,使 Web、小程序、App 上的用户状态(如购物车、收藏、阅读进度)保持一致。

参考答案

同步原则:

  1. 服务端为唯一真相源:所有端最终都以服务端数据为准。
  2. 最小化本地状态:只缓存必要数据,避免多端冲突。
  3. 事件驱动 + 拉取结合:写操作成功后广播事件,各端按需拉取。
  4. 冲突可预期:明确最终一致性策略,必要时提示用户。

同步方案:

text
Web / 小程序 / App

    ├─ 本地缓存(离线、加速)


同步层(Sync Layer)

    ├─ 上传本地变更队列
    ├─ 拉取服务端变更
    ├─ 冲突检测与合并


服务端( truth source )

    ├─ 长连接 / 推送 / 轮询通知变更

关键技术:

  1. 变更队列(Change Queue)

    • 本地操作按时间顺序排队,联网后同步。
    • 每条变更包含操作类型、目标 ID、版本号、时间戳。
  2. 版本向量 / 时间戳

    • lastModifiedAtversion 判断数据新旧。
    • Last-Write-Wins 或自定义合并策略。
  3. 推送通道

    • WebSocket、SSE、长轮询、小程序 socket、App push。
    • 用于实时通知其他端数据变化。
  4. 拉取策略

    • 应用启动、页面可见、用户主动刷新时拉取。
    • 使用 SWR 策略减少请求。
  5. 冲突处理

    • 简单字段:LWW 或版本号拒绝。
    • 复杂对象:服务端合并或用户确认。

示例(阅读进度同步):

ts
// 本地变更队列
interface ReadingProgressChange {
  bookId: string;
  progress: number;
  timestamp: number;
  version: number;
}

// 同步时
async function syncProgress(changes: ReadingProgressChange[]) {
  const serverProgress = await api.getReadingProgress();
  const merged = mergeByTimestamp(changes, serverProgress);
  await api.saveReadingProgress(merged);
}

各端差异处理:

  • Web:SWR + localStorage/IndexedDB。
  • 小程序:storage + 云函数/推送。
  • App:SQLite + 原生推送 + 后台同步。

评分维度

  • 能提出服务端为真相源、事件驱动 + 拉取结合(30%)
  • 能设计变更队列和版本机制(25%)
  • 能说明推送通道和各端差异(25%)
  • 能提到冲突处理策略(20%)

常见错误

  • 以本地状态为准,多端各自为政。
  • 没有版本号或时间戳,冲突无法判断。
  • 所有数据都实时推送,造成性能浪费。

延伸追问

  • 如果某端长期离线,恢复后如何合并大量变更?
  • 用户在一个端删除数据,另一个端如何感知?

相关题目

参考资源

口头回答版

多端同步要以服务端为唯一真相源。本地只做缓存和离线队列。写操作成功后通过 WebSocket、推送或轮询通知其他端,各端再按需拉取。每条变更要带时间戳或版本号,用于冲突判断。简单字段用最后写入为准,复杂对象可以服务端合并或让用户确认。Web 用 SWR 加 IndexedDB,小程序用 storage 加云函数,App 用 SQLite 加原生推送。


FB-29-CP-R-029:如何评估和选择状态管理库?请给出决策矩阵

题型:综合开放题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:状态管理、选型、决策矩阵、Redux、Zustand、Pinia、React Query 出现频率:高频 预计回答时长:15-30 分钟

题目描述: 请从多个维度分析状态管理库的选型因素,并给出一份针对 React / Vue 项目的决策矩阵。

参考答案

选型维度:

  1. 状态类型:客户端状态 vs 服务端状态。
  2. 团队规模与熟悉度:小团队用简单库,大团队需要规范和生态。
  3. TypeScript 支持:类型推导是否友好。
  4. 学习曲线:上手成本和维护成本。
  5. 性能:订阅粒度、重渲染范围。
  6. 生态与工具:DevTools、中间件、持久化、调试。
  7. 可维护性:代码组织、模块化、测试性。
  8. 长期支持:社区活跃度、官方维护情况。

React 项目决策矩阵:

场景推荐方案理由
服务端状态为主TanStack Query / SWR缓存、失效、乐观更新内置
简单全局状态Zustand / Jotai轻量、易用
复杂全局状态、多团队协作Redux Toolkit规范、生态、调试
需要细粒度原子状态Jotai / Recoil按需订阅、派生灵活
表单状态React Hook Form / Formik专门处理表单高频更新
复杂流程状态XState状态机消除非法状态

Vue 项目决策矩阵:

场景推荐方案理由
服务端状态Vue Query / SWR同 React 生态
全局状态PiniaVue 官方推荐、TS 友好
简单全局状态Pinia 或 provide/inject根据复杂度选择
表单状态VeeValidate + Composition API或 Element Plus 表单
复杂流程XState Vue状态机管理

通用决策流程:

text
1. 区分服务端状态和客户端状态
   ├─ 服务端状态 -> React Query / SWR / Vue Query
   └─ 客户端状态 -> 继续判断
2. 客户端状态按复杂度
   ├─ 简单、局部 -> useState / provide-inject
   ├─ 跨组件共享、中等复杂度 -> Zustand / Pinia / Jotai
   └─ 复杂、多团队、强规范 -> Redux Toolkit
3. 特殊场景
   ├─ 表单 -> React Hook Form / VeeValidate
   └─ 复杂流程 -> XState

评分维度

  • 能从状态类型、团队、TS、性能、生态等维度分析(40%)
  • 能给出 React / Vue 的具体决策矩阵(30%)
  • 能说明决策流程和选型原则(20%)
  • 能指出避免一刀切的思路(10%)

常见错误

  • 所有项目都用 Redux 或所有项目都用 Zustand。
  • 忽略服务端状态管理库,把所有数据放全局 Store。
  • 选型只看流行度,不看团队能力和业务场景。

延伸追问

  • 如果一个项目同时需要 Redux 和 React Query,如何划分职责?
  • 状态管理库选型后,如何保证团队一致使用?

相关题目

参考资源

口头回答版

选型要先分清楚是服务端状态还是客户端状态。服务端状态用 React Query 或 SWR;客户端状态再看复杂度:简单用 useState 或 Zustand/Pinia,复杂用 Redux Toolkit。表单单独用 React Hook Form,复杂流程用 XState。还要看团队熟悉度、TypeScript 支持、生态和长期维护。不要一刀切,服务端状态和客户端状态最好分开管。


FB-29-SD-R-030:设计一个支持离线优先的协作编辑状态同步系统

题型:系统设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理 标签:离线优先、协作编辑、CRDT、OT、状态同步、冲突解决 出现频率:低频 预计回答时长:15-30 分钟

题目描述: 请设计一个支持离线优先的多人协作编辑系统,要求支持离线编辑、断网重连、多端同步、冲突自动解决。

参考答案

核心挑战:

  1. 用户离线时仍能编辑。
  2. 多人同时编辑同一文档。
  3. 断网重连后本地操作需要同步。
  4. 冲突自动解决,避免用户感知。
  5. 多端(Web、App、桌面端)数据一致。

技术选型:

  • CRDT(推荐):Yjs、Automerge。天然支持离线优先和最终一致,无需中心服务器协调。
  • OT:Google Docs 早期方案,需要中心服务器转换操作,离线支持较复杂。

系统架构:

text
┌─────────────────────────────────────────┐
│           Editor UI                     │
│  (ProseMirror / Quill / Slate / Monaco) │
├─────────────────────────────────────────┤
│  CRDT Document (Y.Doc / Automerge)      │
├─────────────────────────────────────────┤
│  Local Persistence (IndexedDB)          │
├─────────────────────────────────────────┤
│  Sync Layer (Yjs provider / WebSocket)  │
├─────────────────────────────────────────┤
│  Collaboration Server                   │
├─────────────────────────────────────────┤
│  Persistence (Database / Object Storage)│
└─────────────────────────────────────────┘

关键设计:

  1. 文档模型 CRDT

    • 每个字符/段落是一个 CRDT 元素。
    • 插入、删除、格式化操作都生成 CRDT 操作。
    • 任意顺序合并后结果一致。
  2. 本地持久化

    • 每次本地更新先写入 IndexedDB。
    • 离线时操作继续排队。
  3. 同步协议

    • 使用 Yjs 的 y-websockety-webrtc
    • 连接时交换状态向量(state vector),只同步差异。
  4. 离线队列

    • 记录本地操作日志。
    • 联网后按顺序应用并广播。
  5. 光标与选区

    • 使用相对位置(Yjs 中的 relative position)避免插入删除时光标漂移。
    • 共享 awareness 状态显示协作者光标和昵称。
  6. 版本历史

    • 基于 CRDT 操作日志实现撤销重做和时间轴。

示例(Yjs 核心 API):

ts
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

const doc = new Y.Doc();
const yText = doc.getText('content');

// 本地编辑
yText.insert(0, 'hello');

// 持久化到 IndexedDB
const provider = new WebsocketProvider('wss://demo.yjs.dev', 'room', doc);

冲突解决:

  • CRDT 保证最终一致,无需额外冲突解决。
  • 对于非 CRDT 数据(如元数据),使用版本号或 LWW。

评分维度

  • 能选择 CRDT 并说明其离线优先优势(30%)
  • 能设计本地持久化 + 同步层架构(25%)
  • 能说明光标、选区、awareness 等协作细节(20%)
  • 能提到版本历史和撤销重做(15%)
  • 能说明与 OT 的对比(10%)

常见错误

  • 使用传统 diff 合并文本,导致冲突难以解决。
  • 没有本地持久化,离线编辑丢失。
  • 忽略光标同步和用户体验。

延伸追问

  • 如果某个用户长期离线,重连后操作量很大,如何优化同步?
  • 如何防止恶意用户通过 CRDT 生成大量垃圾数据?

相关题目

参考资源

口头回答版

离线优先的协作编辑系统我推荐用 CRDT,比如 Yjs 或 Automerge,它们天然支持离线编辑和最终一致。架构上:编辑器下面是 CRDT 文档,再下面是 IndexedDB 本地持久化,然后是同步层用 WebSocket 和其他客户端同步。离线时操作本地排队,联网后交换差异。光标和选区要用相对位置避免漂移,awareness 用来显示协作者。CRDT 比 OT 更适合离线场景,因为不需要中心服务器做转换。

FB-29-SD-R-031:如何设计前端 URL 状态管理方案?

题型:系统设计题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理、15 React、16 Vue 标签:URL 状态、路由状态、状态管理、可分享、可回退 出现频率:中频 预计回答时长:15-30 分钟

题目描述: 请设计一个前端 URL 状态管理方案,说明哪些状态适合放入 URL,并给出实现思路和注意事项。

参考答案

URL 是状态的天然容器。将部分状态放入 URL 的好处:

  1. 可分享:用户复制链接即可复现当前页面状态。
  2. 可回退:浏览器前进/后退能恢复状态。
  3. 可书签化:特定筛选条件、分页可直接收藏。
  4. SEO 友好:搜索引擎可抓取部分 URL 状态。

适合放入 URL 的状态:

状态类型示例
搜索条件keyword、filters
分页排序page、pageSize、sortBy、sortOrder
视图切换tab、mode、layout
选中项selectedId、panel
时间范围startDate、endDate

不适合放入 URL 的状态:

  • 表单输入中未提交的草稿。
  • 临时 UI 状态,如弹窗开关、加载状态。
  • 敏感信息,如 token、用户密码。
  • 过大的对象或数组。

实现思路:

  1. 路由参数(Path / Query)

    • React Router:useSearchParamsuseParams
    • Vue Router:useRouteuseRouter
  2. 状态与 URL 双向绑定

    • 状态变化时更新 URL。
    • URL 变化时解析并同步到状态。
  3. 序列化与反序列化

    • 数组、对象使用 JSON.stringify 或自定义编码。
    • 注意 URL 长度限制和可读性。
  4. 状态重置

    • 提供“重置筛选”按钮,清除 URL 参数。

React 示例:

tsx
import { useSearchParams } from 'react-router-dom';

function ProductList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const keyword = searchParams.get('keyword') || '';
  const page = Number(searchParams.get('page')) || 1;

  const setKeyword = (value: string) => {
    setSearchParams({ keyword: value, page: '1' });
  };

  return <input value={keyword} onChange={(e) => setKeyword(e.target.value)} />;
}

注意事项:

  • 避免把过多状态塞入 URL,导致链接过长、难以维护。
  • URL 参数变更可能触发不必要的请求,需防抖或 memo。
  • 注意类型转换和默认值处理。

评分维度

  • 能说明 URL 状态的优势(可分享、可回退、SEO)(25%)
  • 能区分适合与不适合放入 URL 的状态(25%)
  • 能给出双向绑定和序列化方案(25%)
  • 能提到敏感信息、URL 长度等注意事项(25%)

常见错误

  • 把所有状态都放入 URL。
  • 忽略 URL 参数的类型转换,导致 page=NaN 等问题。
  • 在 URL 中暴露敏感信息。

延伸追问

  • 如果 URL 状态和全局 Store 状态冲突,以谁为准?
  • 如何设计一个通用的 URL 状态同步 Hook?

相关题目

参考资源

口头回答版

URL 本身就是很好的状态容器,适合放搜索条件、分页、排序、当前 tab 这些。好处是链接可以分享、可以回退、可以收藏。不适合放草稿、临时弹窗、敏感信息。实现上就是状态和 URL 双向绑定:状态变了更新 URL,URL 变了同步回状态。React 用 useSearchParams,Vue 用 useRoute。要注意类型转换、默认值,还有别把链接搞太长。


FB-29-PE-R-032:全局状态管理下如何进行性能优化?

题型:性能优化题 难度:⚫ 架构 岗位层级:架构师 面试知识域:29 数据与状态管理、15 React、16 Vue 标签:状态管理、性能优化、按需订阅、selector、拆分状态 出现频率:高频 预计回答时长:15-30 分钟

题目描述: 请分析全局状态管理下常见的性能问题,并给出优化策略和实现方案。

参考答案

全局状态管理常见性能问题:

  1. 不必要的重渲染

    • 全局状态变化时,所有订阅组件都重新渲染。
    • 解决:按需订阅、selector、拆分状态。
  2. 状态粒度过大

    • 一个巨大对象作为全局状态,任何字段变化都触发全量更新。
    • 解决:按业务域拆分为多个 Store / Slice / Atom。
  3. 频繁更新的状态和不常变状态混合

    • 例如把计时器和用户信息放在同一个 Context。
    • 解决:拆分高频与低频状态。
  4. 派生状态重复计算

    • 每次渲染都重新计算过滤、排序结果。
    • 解决:使用 selector、computed、useMemo。
  5. 大列表或大数据量

    • 全局存储大量数据导致内存和渲染压力。
    • 解决:虚拟滚动、分页、服务端状态库缓存、数据裁剪。

优化策略:

问题方案
不必要重渲染selector(Zustand/Redux)、原子化(Jotai/Recoil)、拆分 Context
状态粒度过大按模块/业务域拆分 Store
高频与低频混合拆分高频状态到独立 Store/Atom
派生重复计算computed、reselect、useMemo
大数据量虚拟滚动、分页、服务端状态库

Zustand selector 示例:

tsx
// 只订阅 count,name 变化不会触发重渲染
const count = useStore((state) => state.count);

Redux Reselect 示例:

ts
import { createSelector } from '@reduxjs/toolkit';

const selectTodos = (state) => state.todos.items;
const selectFilter = (state) => state.todos.filter;

const selectVisibleTodos = createSelector(
  [selectTodos, selectFilter],
  (todos, filter) => todos.filter((todo) => todo.status === filter)
);

Context 拆分示例:

tsx
// 避免一个 Context 包含所有状态
const ThemeContext = createContext();
const UserContext = createContext();

最佳实践:

  • 全局状态最小化,优先局部化。
  • 使用 DevTools 和 React Profiler 定位重渲染。
  • 服务端状态不要存入全局客户端 Store。

评分维度

  • 能识别不必要重渲染、状态粒度过大等性能问题(30%)
  • 能给出 selector、拆分状态、computed 等优化方案(30%)
  • 能结合具体库(Zustand/Redux/Jotai)给出代码示例(20%)
  • 能说明全局状态最小化和使用分析工具(20%)

常见错误

  • 所有组件都订阅整个 Store。
  • 用 Context 管理所有状态,导致 value 变化时全部消费者重渲染。
  • 为了优化而过度拆分,增加维护成本。

延伸追问

  • 如何量化状态管理性能优化的收益?
  • 在 Redux 中,reselect 如何避免重复计算?

相关题目

参考资源

口头回答版

全局状态管理常见的性能问题有不必要的重渲染、状态粒度太大、高频和低频状态混在一起、派生状态重复计算。优化方法包括:用 selector 只订阅需要的字段,比如 Zustand 的 useStore(selector);把大 Store 按业务域拆分;高频状态单独放;派生状态用 computed 或 reselect 缓存。还要尽量少放全局状态,多用 React Profiler 找问题。

FB-29-SS-B-001:前端应用中有哪些类型的状态?分别应该怎么管理?

题型:软技能题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:3-5 分钟

题目描述: 前端应用中有哪些类型的状态?分别应该怎么管理。

参考答案

前端状态通常分为五类,不同类型的状态有不同的生命周期、更新来源和一致性要求:

  1. 本地 UI 状态:只影响单个组件,如弹窗开关、表单临时值。用 useState / useReducer 管理。
  2. 全局共享状态:多个组件共享,如用户信息、主题、语言。用 Zustand / Redux / Pinia / Context 管理。
  3. 服务端状态:来自服务端的数据,如列表、详情。用 React Query / SWR / TanStack Query 管理。
  4. URL 状态:页面级可分享状态,如筛选条件、页码。用路由参数 / query string 管理。
  5. 表单状态:复杂表单内部状态。用 React Hook Form / Formik / VeeValidate 管理。

分类管理的意义:避免所有状态都放入全局 Store 导致的耦合和性能问题;服务端状态有缓存、失效、去重等专属需求,应使用专门库。

评分维度

  • 分类完整(30%)
  • 管理方案合理(30%)
  • 能解释分类原因(20%)
  • 有项目经验(20%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

前端状态通常分为五类,不同类型的状态有不同的生命周期、更新来源和一致性要求: 1. 本地 UI 状态:只影响单个组件,如弹窗开关、表单临时值。 用 useState / useReducer 管理。 2. 全局共享状态:多个组件共享,如用户信息、主题、语言。 用 Zustand / Redux / Pinia / Context 管理。


FB-29-CO-B-009:Redux、Zustand、React Query 分别解决什么问题?可以互相替代吗?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:3-5 分钟

题目描述: Redux、Zustand、React Query 分别解决什么问题?可以互相替代吗。

参考答案

三者定位不同:

  • Redux:全局客户端状态管理,强调可预测、可调试,适合超大型应用和需要严格状态流控制的场景。
  • Zustand:轻量全局状态管理,API 简单灵活,适合中小型应用或作为 Redux 的简化替代。
  • React Query:服务端状态管理,解决数据获取、缓存、失效、去重、乐观更新等问题。

它们不能互相替代

  • Redux/Zustand 不擅长处理服务端数据的缓存、失效和异步同步。
  • React Query 不擅长管理纯客户端 UI 状态(如主题、弹窗)。

最佳实践是组合使用:Zustand 管理全局客户端状态,React Query 管理服务端状态,局部状态用 useState。

评分维度

  • 能区分三者定位(30%)
  • 能说明不可替代性(30%)
  • 提出组合方案(20%)
  • 有选型经验(20%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

三者定位不同: - Redux:全局客户端状态管理,强调可预测、可调试,适合超大型应用和需要严格状态流控制的场景。 - Zustand:轻量全局状态管理,API 简单灵活,适合中小型应用或作为 Redux 的简化替代。 - React Query:服务端状态管理,解决数据获取、缓存、失效、去重、乐观更新等问题。 它们不能互相替代: - Redux/Zustand 不擅长处理服务端数据的缓存、失效和异步同步。


FB-29-CP-B-001:React Query 的 staleTime 和 cacheTime 有什么区别?

题型:综合开放题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:3-5 分钟

题目描述: React Query 的 staleTime 和 cacheTime 有什么区别。

参考答案

  • staleTime:数据被视为“新鲜”的时间。在 staleTime 内,组件重新挂载不会触发新请求;超过后,组件重新挂载会 background refetch。
  • cacheTime:数据在缓存中保留的时间。超过 cacheTime 后,即使还有组件引用该 query,未激活的数据也会被垃圾回收。

比喻:

  • staleTime 是牛奶的“最佳饮用期”,过期了还可以喝,但会检查一下是否变质。
  • cacheTime 是牛奶的“保质期”,过期后直接倒掉。

配置建议:

  • 变化频繁的数据:staleTime 设短(如 0)。
  • 变化少的数据:staleTime 设长(如 5 分钟),cacheTime 可适当延长。
tsx
useQuery({
  queryKey: ['user', userId],
  queryFn: fetchUser,
  staleTime: 1000 * 60 * 5, // 5 分钟内视为新鲜
  cacheTime: 1000 * 60 * 30, // 30 分钟后清理缓存
});

评分维度

  • 概念准确(35%)
  • 能举例说明(25%)
  • 知道如何配置(20%)
  • 能说明适用场景(20%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

  • staleTime:数据被视为“新鲜”的时间。 在 staleTime 内,组件重新挂载不会触发新请求;超过后,组件重新挂载会 background refetch。 - cacheTime:数据在缓存中保留的时间。 超过 cacheTime 后,即使还有组件引用该 query,未激活的数据也会被垃圾回收。

FB-29-CO-B-010:什么是乐观更新?实现时需要注意什么?

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:3-5 分钟

题目描述: 什么是乐观更新?实现时需要注意什么。

参考答案

乐观更新是先假设操作会成功,立即更新 UI,再异步发送请求。如果失败,再回滚到之前的状态。它能显著提升用户体验,减少等待感。

实现注意事项:

  1. 保存旧状态:用于失败时回滚。
  2. 取消冲突 refetch:在 onMutate 中取消正在进行的相关查询,避免冲突。
  3. 执行回滚:在 onError 中恢复旧状态。
  4. 最终校验:在 onSettled 中 invalidate 相关 query,确保数据正确。
  5. 适用边界:对于关键操作(如支付、下单),不适合乐观更新,应等待服务端确认。
tsx
const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const previousTodos = queryClient.getQueryData(['todos']);
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);
    return { previousTodos };
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

评分维度

  • 能解释原理(25%)
  • 能说出实现步骤(30%)
  • 能指出注意事项(20%)
  • 有代码示例(10%)
  • 有实战经验(15%)

二、进阶题

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

乐观更新是先假设操作会成功,立即更新 UI,再异步发送请求。 如果失败,再回滚到之前的状态。 它能显著提升用户体验,减少等待感。 实现注意事项: 1. 保存旧状态:用于失败时回滚。


FB-29-SD-A-001:如何设计一个大型列表页的状态管理?

题型:系统设计题 难度:🟡 进阶 岗位层级:高级 / 资深 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 如何设计一个大型列表页的状态管理。

参考答案

大型列表页的状态需要按职责分层管理,避免全部塞入全局 Store。

状态划分:

  • 筛选条件、排序、页码:URL 状态(可分享、可回退、刷新不丢失)。
  • 列表数据:React Query 服务端状态,自动处理缓存、加载、错误。
  • 加载/错误状态:React Query 自动管理。
  • 选中行、展开状态:本地 UI 状态。
  • 批量操作结果:临时本地状态,操作成功后 invalidate 列表。

关键设计:

  • queryKey 要包含所有筛选参数,确保不同参数走不同缓存。
  • 使用分页或无限滚动时,注意缓存策略和预加载。
  • 用户操作后主动 invalidate 列表数据。
  • 对高频筛选变化做防抖,避免频繁请求。
tsx
const [search, setSearch] = useSearchParams();
const page = Number(search.get('page')) || 1;
const keyword = search.get('keyword') || '';

const { data, isLoading } = useQuery({
  queryKey: ['orders', page, keyword],
  queryFn: () => fetchOrders({ page, keyword }),
  keepPreviousData: true,
});

评分维度

  • 状态分类合理(30%)
  • queryKey 设计正确(25%)
  • 考虑用户体验(20%)
  • 有代码示例(10%)
  • 有实际项目经验(15%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

大型列表页的状态需要按职责分层管理,避免全部塞入全局 Store。 状态划分: - 筛选条件、排序、页码:URL 状态(可分享、可回退、刷新不丢失)。 - 列表数据:React Query 服务端状态,自动处理缓存、加载、错误。 - 加载/错误状态:React Query 自动管理。


FB-29-SS-A-001:服务端状态管理和客户端状态管理有什么区别?

题型:软技能题 难度:🟡 进阶 岗位层级:高级 / 资深 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 服务端状态管理和客户端状态管理有什么区别。

参考答案

服务端状态的特殊性:

  1. 真相在服务端:前端只是缓存,最终一致性以服务端为准。
  2. 获取是异步的:有 loading、error、success 等状态。
  3. 需要缓存策略:如 staleTime、cacheTime、refetchOnWindowFocus。
  4. 需要失效策略:Mutation 后 invalidate 相关 Query。
  5. 需要去重:多个组件同时请求同一数据,应只发一次请求。
  6. 需要乐观更新和回滚:提升用户体验。
  7. 需要错误重试:网络波动时自动重试。
  8. 需要后台刷新:页面重新聚焦时自动同步最新数据。

因此服务端状态需要专门的管理方案(React Query / SWR / TanStack Query / RTK Query),而不是简单用 Redux 存储。

评分维度

  • 能说出核心区别(35%)
  • 能举例说明(25%)
  • 能解释为什么需要专用库(25%)
  • 有项目经验(15%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

服务端状态的特殊性: 1. 真相在服务端:前端只是缓存,最终一致性以服务端为准。 2. 获取是异步的:有 loading、error、success 等状态。 3. 需要缓存策略:如 staleTime、cacheTime、refetchOnWindowFocus。 4. 需要失效策略:Mutation 后 invalidate 相关 Query。


FB-29-CO-A-017:如何处理数据一致性问题?

题型:概念题 难度:🟡 进阶 岗位层级:高级 / 资深 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 如何处理数据一致性问题。

参考答案

数据一致性处理策略应根据业务要求选择:

  1. 主动失效:Mutation 后 invalidate 相关 Query,让数据重新获取。
  2. 手动更新缓存:直接更新本地缓存,避免重新请求,适合频繁操作场景。
  3. 乐观更新 + 回滚:提升用户体验,失败时恢复旧状态。
  4. 实时同步:WebSocket / SSE 推送数据变化,适合协同场景。
  5. 版本号/时间戳:检测冲突,决定以哪方为准,适合并发编辑。
  6. 最终一致性:接受短暂不一致,通过同步机制最终一致。

选择依据:

  • 业务对一致性的要求(强一致 vs 最终一致)。
  • 用户体验要求。
  • 技术复杂度和成本。

例如:电商下单后应立即刷新订单列表(主动失效);社交点赞可用乐观更新;协同文档需用 OT/CRDT 保证一致性。

评分维度

  • 策略覆盖全面(35%)
  • 能区分场景(25%)
  • 有架构权衡意识(20%)
  • 有实际经验(20%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

数据一致性处理策略应根据业务要求选择: 1. 主动失效:Mutation 后 invalidate 相关 Query,让数据重新获取。 2. 手动更新缓存:直接更新本地缓存,避免重新请求,适合频繁操作场景。 3. 乐观更新 + 回滚:提升用户体验,失败时恢复旧状态。 4. 实时同步:WebSocket / SSE 推送数据变化,适合协同场景。


FB-29-SD-A-002:如何设计一个支持乐观更新的全局状态方案?

题型:系统设计题 难度:🟡 进阶 岗位层级:高级 / 资深 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 如何设计一个支持乐观更新的全局状态方案。

参考答案

设计乐观更新方案需要同时考虑性能、一致性和回滚能力。

核心设计:

  1. 状态分层:服务端状态用 React Query 管理,乐观更新在其缓存层实现;纯 UI 状态不需要乐观更新。
  2. 操作原子化:每个 mutation 对应一个明确的业务操作,避免批量操作部分失败难以回滚。
  3. 快照机制:执行乐观更新前保存当前状态快照。
  4. 冲突检测:对同一资源的并发操作,检测是否会产生冲突。
  5. 队列化:对高频操作(如连续点赞、拖拽排序)可以队列化,避免中间状态频繁闪烁。
  6. 错误回滚:请求失败时恢复到快照状态,并提示用户。
  7. 最终同步:操作成功后 invalidate 或手动更新缓存,确保与服务端一致。
ts
interface OptimisticUpdate<T> {
  key: string;
  previous: T;
  optimistic: T;
  mutation: () => Promise<T>;
  onError: (err: Error) => void;
}

评分维度

  • 设计方案完整(30%)
  • 能说明快照和回滚(25%)
  • 考虑并发和队列化(20%)
  • 有代码或架构示例(15%)
  • 有实战经验(10%)

三、高级题

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

设计乐观更新方案需要同时考虑性能、一致性和回滚能力。 核心设计: 1. 状态分层:服务端状态用 React Query 管理,乐观更新在其缓存层实现;纯 UI 状态不需要乐观更新。 2. 操作原子化:每个 mutation 对应一个明确的业务操作,避免批量操作部分失败难以回滚。 3. 快照机制:执行乐观更新前保存当前状态快照。


FB-29-SD-P-001:设计一个支持离线编辑的协同文档应用的数据层。

题型:系统设计题 难度:🟣 深入 岗位层级:资深 / 架构 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:7-10 分钟

题目描述: 设计一个支持离线编辑的协同文档应用的数据层。。

参考答案

离线协同文档的数据层需要兼顾本地优先、冲突解决和实时同步。

架构要素:

  1. 本地优先:使用 IndexedDB 存储文档快照和编辑操作队列。
  2. 操作队列:记录离线期间的所有操作(insert、delete、format)。
  3. 冲突解决:使用 OT(Operational Transformation)或 CRDT(如 Yjs)处理多人并发编辑。
  4. 实时同步:WebSocket 同步操作,网络恢复后批量同步离线队列。
  5. 版本管理:每个文档有版本号或向量时钟,用于检测冲突。
  6. 服务端持久化:最终保存到数据库,作为权威来源。
  7. 数据丢失防护:定期快照、本地备份、上传进度提示。

挑战与应对:

  • 冲突合并:优先使用 CRDT 保证最终一致性。
  • 网络恢复后的批量同步:分批上传,避免一次性发送大量操作。
  • 多人同时编辑的实时一致性:操作广播 + 本地合并。
  • 数据丢失:本地快照 + 服务端持久化双保险。

评分维度

  • 架构完整(30%)
  • 技术选型合理(25%)
  • 考虑冲突和同步(25%)
  • 有深度思考(20%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

离线协同文档的数据层需要兼顾本地优先、冲突解决和实时同步。 架构要素: 1. 本地优先:使用 IndexedDB 存储文档快照和编辑操作队列。 2. 操作队列:记录离线期间的所有操作(insert、delete、format)。 3. 冲突解决:使用 OT(Operational Transformation)或 CRDT(如 Yjs)处理多人并发编辑。


FB-29-SD-P-002:如何设计一个大型前端应用的状态管理架构?

题型:系统设计题 难度:🟣 深入 岗位层级:资深 / 架构 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:7-10 分钟

题目描述: 如何设计一个大型前端应用的状态管理架构。

参考答案

大型前端应用的状态管理应遵循“分层、分域、分离”的原则。

架构设计:

  1. 状态分层
    • 局部 UI 状态:组件内部。
    • 领域状态:按业务领域拆分 Store(如订单域、用户域)。
    • 全局共享状态:主题、语言、用户信息等。
    • 服务端状态:React Query / SWR。
  2. 按领域组织:每个领域有独立的 actions、selectors、effects,避免全局命名冲突。
  3. 服务端与客户端状态分离:不要把 API 数据长期放在 Redux 中,交给专业库管理。
  4. 不可变数据:使用 Immer 或不可变更新,便于调试和性能优化。
  5. 派生状态:用 selector / computed 派生,避免冗余存储。
  6. 状态变更可追踪: Redux DevTools / Zustand DevTools / 日志。
  7. 性能优化:按订阅拆分、使用 selector 避免不必要的重渲染。
ts
// 领域状态示例
interface UserState {
  profile: UserProfile | null;
  permissions: string[];
}

const useUserStore = create<UserState & UserActions>((set) => ({
  profile: null,
  permissions: [],
  setProfile: (profile) => set({ profile }),
}));

评分维度

  • 架构分层清晰(30%)
  • 领域组织合理(25%)
  • 服务端与客户端分离(20%)
  • 有代码示例(10%)
  • 有大型项目经验(15%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

大型前端应用的状态管理应遵循“分层、分域、分离”的原则。 架构设计: 1. 状态分层: - 局部 UI 状态:组件内部。 - 领域状态:按业务领域拆分 Store(如订单域、用户域)。 - 全局共享状态:主题、语言、用户信息等。


FB-29-SC-P-001:Redux Toolkit、Zustand、Jotai、Recoil 的适用场景分别是什么?

题型:场景设计题 难度:🟣 深入 岗位层级:资深 / 架构 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:7-10 分钟

题目描述: Redux Toolkit、Zustand、Jotai、Recoil 的适用场景分别是什么。

参考答案

特点适用场景
Redux Toolkit生态成熟、可预测、调试强大型应用、复杂状态流、严格规范
ZustandAPI 极简、无样板代码、TypeScript 友好中小型应用、快速开发
Jotai原子化状态、组合灵活、按需订阅需要细粒度状态共享的场景
Recoil原子化 + selector、Facebook 出品React 生态、需要派生状态管理

选型建议:

  • 团队熟悉 Redux 且项目规模大 → Redux Toolkit。
  • 追求简洁和快速开发 → Zustand。
  • 需要原子化、组合式状态 → Jotai。
  • 已有 Recoil 使用经验 → Recoil(注意生态活跃度)。

共同趋势:现代状态管理更强调按需订阅、TypeScript 支持和最小化样板代码。

评分维度

  • 能区分四种库(35%)
  • 能说明适用场景(30%)
  • 有选型依据(20%)
  • 了解生态趋势(15%)

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

| 库 | 特点 | 适用场景 | |----|------|---------| | Redux Toolkit | 生态成熟、可预测、调试强 | 大型应用、复杂状态流、严格规范 | | Zustand | API 极简、无样板代码、TypeScript 友好 | 中小型应用、快速开发 | | Jotai | 原子化状态、组合灵活、按需订阅 | 需要细粒度状态共享的场景 | | Recoil | 原子化 + selector、Facebook 出品 | React 生态、需要派生状态管理 | 选型建议: - 团队熟悉 Redux 且项目规模大 → Redux Toolkit。 - 追求简洁和快速开发 → Zustand。 - 需要原子化、组合式状态 → Jotai。 - 已有 Recoil……


FB-29-CO-P-025:如何处理跨组件、跨页面的状态共享与持久化?

题型:概念题 难度:🟣 深入 岗位层级:资深 / 架构 面试知识域:29 数据与状态管理 标签:状态管理、React Query、Redux、Zustand、缓存 出现频率:中频 预计回答时长:7-10 分钟

题目描述: 如何处理跨组件、跨页面的状态共享与持久化。

参考答案

跨组件、跨页面状态共享与持久化需要统一方案:

  1. 全局状态库:Zustand / Redux / Pinia 管理跨页面共享状态。
  2. URL 状态:适合可分享、可刷新的页面级状态。
  3. 本地存储持久化
    • 使用 localStorage / sessionStorage / IndexedDB。
    • 注意序列化、版本迁移、容量限制。
    • 敏感信息不要持久化到 localStorage。
  4. 状态同步
    • 多标签页同步:BroadcastChannel / storage 事件。
    • 前后端同步:WebSocket / SSE / 轮询。
  5. 持久化策略
    • 只持久化必要状态(如用户偏好)。
    • 服务端权威数据不持久化,只缓存。
    • 持久化前做 schema 校验,防止版本不兼容。
ts
// Zustand 持久化示例
import { persist } from 'zustand/middleware';

const useStore = create(persist(
  (set) => ({ theme: 'light', setTheme: (theme) => set({ theme }) }),
  { name: 'app-theme' }
));

评分维度

  • 方案覆盖全面(30%)
  • 能区分共享和持久化(25%)
  • 考虑安全和版本(20%)
  • 有代码示例(10%)
  • 有实战经验(15%)

面试准备建议

  1. 理解状态分类:本地、全局、服务端、URL、表单状态各司其职。
  2. 掌握 React Query / SWR 核心概念:queryKey、staleTime、cacheTime、invalidate、乐观更新。
  3. 能设计列表页、详情页、表单页的状态管理方案。
  4. 理解数据一致性策略:主动失效、乐观更新、实时同步、最终一致。
  5. 准备项目案例:说明你如何选型状态管理库、解决性能或一致性问题。

领域编号:A05 数据与状态管理
最后更新:2026-06-18

常见错误

  • 回答停留在定义复述,缺少真实项目中的取舍与折中。
  • 只讲正常路径,不提超时、降级、兼容等边界情况。
  • 对关键指标和取舍缺乏量化意识。

口头回答版

跨组件、跨页面状态共享与持久化需要统一方案: 1. 全局状态库:Zustand / Redux / Pinia 管理跨页面共享状态。 2. URL 状态:适合可分享、可刷新的页面级状态。 3. 本地存储持久化: - 使用 localStorage / sessionStorage / IndexedDB。 - 注意序列化、版本迁移、容量限制。

FB-29-CO-B-011:前端状态管理要解决什么问题

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态 标签:状态管理、数据流、组件通信、可预测性 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明前端状态管理的本质和常见场景。

参考答案

核心思路如下:

  1. 解决跨组件共享状态、避免 prop drilling
  2. 统一数据流向,提升可预测性和可调试性
  3. 常见场景:用户态、主题、购物车、表单、路由参数
  4. 区分 UI 状态与远端状态
  5. 状态应集中必要、分散合理

需要避免的典型误区:

  • 所有状态都放全局
  • 状态管理过度导致模板代码多
  • 远端状态与本地状态混淆

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):解决跨组件共享状态、避免 prop drilling、统一数据流向,提升可预测性和可调试性 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有状态都放全局
  • 状态管理过度导致模板代码多
  • 远端状态与本地状态混淆

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

解决跨组件共享状态、避免 prop drilling;统一数据流向,提升可预测性和可调试性;常见场景;区分 UI 状态与远端状态;状态应集中必要、分散合理。同时要避免所有状态都放全局。


FB-29-CO-A-018:Redux 与 MobX 的设计哲学差异

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:Redux、MobX、响应式、不可变、状态管理 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请比较 Redux 和 MobX 在数据模型、更新方式和适用场景上的差异。

参考答案

核心思路如下:

  1. Redux:单一不可变状态树,显式 action + reducer,适合复杂协作
  2. MobX:响应式对象,自动追踪依赖,更新隐式,适合快速开发
  3. Redux 强调可预测和调试;MobX 强调简洁和自动化
  4. Redux 模板多但生态强;MobX 学习曲线低但调试需理解依赖
  5. 选型:团队熟悉度、应用规模、可维护性要求

需要避免的典型误区:

  • Redux 用于所有项目
  • MobX 任意修改状态难以追踪
  • 认为两者不能共存

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):Redux、MobX 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • Redux 用于所有项目
  • MobX 任意修改状态难以追踪
  • 认为两者不能共存

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

Redux;MobX;Redux 强调可预测和调试;MobX 强调简洁和自动化;Redux 模板多但生态强;MobX 学习曲线低但调试需理解依赖;选型。同时要避免 Redux 用于所有项目。


FB-29-CO-P-026:集中式状态与局部状态的边界

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:状态、局部状态、全局状态、Context、提升 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请说明哪些状态应该全局管理,哪些应该留在组件局部。

参考答案

核心思路如下:

  1. 全局状态:跨页面/模块共享、用户会话、主题、权限、购物车
  2. 局部状态:表单草稿、UI 展开收起、纯组件内部数据
  3. 远端状态:优先交给专门数据层管理
  4. Context 适合低频更新、作用域明确的状态
  5. 避免为了共享而过度提升状态

需要避免的典型误区:

  • 所有状态提升到 Redux
  • 局部状态滥用导致难以调试
  • Context 高频更新导致大面积渲染

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):全局状态、局部状态 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有状态提升到 Redux
  • 局部状态滥用导致难以调试
  • Context 高频更新导致大面积渲染

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

全局状态;局部状态;远端状态;Context 适合低频更新、作用域明确的状态;避免为了共享而过度提升状态。同时要避免所有状态提升到 Redux。


FB-29-SC-A-001:如何设计前端数据缓存策略

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:数据缓存、SWR、React Query、过期、失效 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请说明前端请求数据缓存的设计要点。

参考答案

核心思路如下:

  1. 区分服务端状态与客户端状态
  2. 使用 SWR/React Query/TanStack Query 管理远端状态
  3. 设置缓存时间、stale-while-revalidate、重新获取策略
  4. 基于 key 做缓存,mutation 后主动失效或乐观更新
  5. 离线场景:持久化缓存 + 重试

需要避免的典型误区:

  • 所有接口都不缓存
  • 缓存过期策略一刀切
  • 更新数据后缓存未失效

补充说明

在实际落地 设计前端数据缓存策略 时,建议结合 数据缓存、SWR、React Query 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):区分服务端状态与客户端状态、使用 SWR/React Query/TanStack Query 管理远端状态 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有接口都不缓存
  • 缓存过期策略一刀切
  • 更新数据后缓存未失效

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

区分服务端状态与客户端状态;使用 SWR/React Query/TanStack Query 管理远端状态;设置缓存时间、stale-while-revalidate、重新获取策略;基于 key 做缓存,mutation 后主动失效或乐观更新;离线场景。同时要避免所有接口都不缓存。


FB-29-SD-R-032:设计一个前端数据同步与状态管理方案

题型:系统设计题 难度:🔴 架构 岗位层级:架构师 / 专家 面试知识域:29 数据与状态 标签:数据同步、状态管理、乐观更新、冲突、离线 出现频率:中频 预计回答时长:15-30 分钟

题目描述: 请为一个多端协作应用设计数据同步与状态管理架构。

参考答案

核心思路如下:

  1. 分层:本地状态、远端状态、同步层、持久化层
  2. 乐观更新:本地先更新,失败回滚
  3. 冲突处理:最后写入获胜、版本向量、业务合并策略
  4. 离线支持:操作队列 + 幂等 key
  5. 实时同步:WebSocket/SSE 推送变更
  6. 状态可观测:日志、回放、时间旅行

需要避免的典型误区:

  • 多端直接读写同一全局状态
  • 乐观更新无回滚机制
  • 冲突处理交给前端复杂化

补充说明

在实际落地 设计一个前端数据同步与状态管理方案 时,建议结合 数据同步、状态管理、乐观更新 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):分层、乐观更新 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 多端直接读写同一全局状态
  • 乐观更新无回滚机制
  • 冲突处理交给前端复杂化

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

分层;乐观更新;冲突处理;离线支持;实时同步;状态可观测。同时要避免多端直接读写同一全局状态。


FB-29-CO-A-019:什么是 CQRS 与 Event Sourcing

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:CQRS、Event Sourcing、命令、查询、事件 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请解释 CQRS 和 Event Sourcing,并说明前端如何配合。

参考答案

核心思路如下:

  1. CQRS:命令和查询分离,读写模型不同
  2. Event Sourcing:以事件序列代替当前状态,状态可重放
  3. 前端配合:命令通过 API 发出,查询使用 BFF 聚合视图
  4. 事件驱动 UI:订阅事件流更新局部状态
  5. 适合审计、复杂业务、实时协作

需要避免的典型误区:

  • 在小应用强行 CQRS
  • 前端直接维护事件流所有状态
  • 忽略最终一致性

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):CQRS、Event Sourcing 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 在小应用强行 CQRS
  • 前端直接维护事件流所有状态
  • 忽略最终一致性

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

CQRS;Event Sourcing;前端配合;事件驱动 UI;适合审计、复杂业务、实时协作。同时要避免在小应用强行 CQRS。


FB-29-SC-P-002:如何处理前端表单复杂状态

题型:场景设计题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:表单、状态、React Hook Form、Formik、校验 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请说明复杂表单状态管理的方案。

参考答案

核心思路如下:

  1. 字段状态:value、 touched、error、dirty
  2. 使用 React Hook Form / Formik / Formily 等库抽象
  3. 表单级与字段级校验分离
  4. 动态表单:schema 驱动渲染与校验
  5. 提交状态:loading、success、error、retry

需要避免的典型误区:

  • 每个字段都单独 useState
  • 校验逻辑散落在组件
  • 表单状态和业务状态混在一起

补充说明

在实际落地 处理前端表单复杂状态 时,建议结合 表单、状态、React Hook Form 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):字段状态、使用 React Hook Form / Formik / Formily 等库抽象 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每个字段都单独 useState
  • 校验逻辑散落在组件
  • 表单状态和业务状态混在一起

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

字段状态;使用 React Hook Form / Formik / Formily 等库抽象;表单级与字段级校验分离;动态表单;提交状态。同时要避免每个字段都单独 useState。


FB-29-CO-P-027:乐观更新与悲观更新的取舍

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:乐观更新、悲观更新、一致性、回滚、体验 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请比较乐观更新和悲观更新,并说明各自适用场景。

参考答案

核心思路如下:

  1. 悲观更新:等服务端返回成功后再更新 UI,安全但慢
  2. 乐观更新:先更新 UI 再请求,体验好但需处理失败
  3. 适用:点赞、收藏、购物车可用乐观;支付、下单用悲观
  4. 失败处理:回滚状态、提示用户、重试
  5. 幂等是乐观更新的基础

需要避免的典型误区:

  • 所有操作都用乐观更新
  • 乐观更新失败不提示
  • 非幂等操作也乐观更新

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):悲观更新、乐观更新 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有操作都用乐观更新
  • 乐观更新失败不提示
  • 非幂等操作也乐观更新

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

悲观更新;乐观更新;适用;失败处理;幂等是乐观更新的基础。同时要避免所有操作都用乐观更新。


FB-29-SC-A-002:如何设计跨组件共享的数据源

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:Context、状态管理、依赖注入、作用域 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计一个可在组件树中共享、支持作用域隔离的数据源方案。

参考答案

核心思路如下:

  1. 使用 Context + useReducer 提供作用域内状态
  2. 状态提升 + props 传递适合小范围
  3. 依赖注入容器:提供可替换的实现
  4. 按业务模块拆分多个 Context,避免全局单 store
  5. 状态变更通过 reducer/action,保持可预测

需要避免的典型误区:

  • 一个全局 Context 包含所有状态
  • Context 高频更新导致性能问题
  • 子组件直接修改 Context 值

补充说明

在实际落地 设计跨组件共享的数据源 时,建议结合 Context、状态管理、依赖注入 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):使用 Context + useReducer 提供作用域内状态、状态提升 + props 传递适合小范围 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 一个全局 Context 包含所有状态
  • Context 高频更新导致性能问题
  • 子组件直接修改 Context 值

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

使用 Context + useReducer 提供作用域内状态;状态提升 + props 传递适合小范围;依赖注入容器;按业务模块拆分多个 Context,避免全局单 store;状态变更通过 reducer/action,保持可预测。同时要避免一个全局 Context 包含所有状态。


FB-29-CO-A-020:什么是规范化状态(Normalized State)

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:规范化状态、Redux、Entity、关系型数据 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请解释状态规范化及其优势。

参考答案

核心思路如下:

  1. 把嵌套数据拆分为独立实体表,通过 ID 引用
  2. 避免数据重复,更新一处全局一致
  3. 便于查询、分页、缓存、乐观更新
  4. 工具:Redux Toolkit createEntityAdapter、Normalizr
  5. 适合列表、关联数据、评论等场景

需要避免的典型误区:

  • 所有状态都深层嵌套
  • 规范化过度导致读取复杂
  • 不使用选择器封装查询

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):把嵌套数据拆分为独立实体表,通过 ID 引用、避免数据重复,更新一处全局一致 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有状态都深层嵌套
  • 规范化过度导致读取复杂
  • 不使用选择器封装查询

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

把嵌套数据拆分为独立实体表,通过 ID 引用;避免数据重复,更新一处全局一致;便于查询、分页、缓存、乐观更新;工具;适合列表、关联数据、评论等场景。同时要避免所有状态都深层嵌套。


FB-29-PE-A-001:状态管理库的性能优化

题型:性能优化题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:状态管理、性能、选择器、memo、订阅 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明如何优化 Redux/Zustand 等状态管理库的性能。

参考答案

核心思路如下:

  1. 使用选择器只订阅需要的切片,避免全量渲染
  2. 拆分 store 或按模块组织
  3. 状态更新保持不可变,避免不必要变更
  4. 使用 Reselect 缓存派生状态
  5. 对大型列表做分页或虚拟化

需要避免的典型误区:

  • 每次返回新对象导致所有组件渲染
  • 在 selector 中做复杂计算
  • 把所有状态放一个原子

补充说明

在实际落地 状态管理库的性能优化 时,建议结合 状态管理、性能、选择器 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):使用选择器只订阅需要的切片,避免全量渲染、拆分 store 或按模块组织 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每次返回新对象导致所有组件渲染
  • 在 selector 中做复杂计算
  • 把所有状态放一个原子

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

使用选择器只订阅需要的切片,避免全量渲染;拆分 store 或按模块组织;状态更新保持不可变,避免不必要变更;使用 Reselect 缓存派生状态;对大型列表做分页或虚拟化。同时要避免每次返回新对象导致所有组件渲染。


FB-29-SC-P-003:如何设计前端权限状态与路由联动

题型:场景设计题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:权限、状态、路由、RBAC、守卫 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请设计前端权限数据与路由、菜单、按钮的联动方案。

参考答案

核心思路如下:

  1. 权限数据作为远端状态,登录后拉取并缓存
  2. 路由守卫根据权限过滤路由和菜单
  3. 按钮/字段级通过高阶组件或 hook 判断
  4. 权限变更时重新计算可见性
  5. 敏感操作服务端鉴权,前端仅做体验控制

需要避免的典型误区:

  • 权限判断写死在前端
  • 前端权限可绕过视为安全控制
  • 权限状态不同步

补充说明

在实际落地 设计前端权限状态与路由联动 时,建议结合 权限、状态、路由 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):权限数据作为远端状态,登录后拉取并缓存、路由守卫根据权限过滤路由和菜单 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 权限判断写死在前端
  • 前端权限可绕过视为安全控制
  • 权限状态不同步

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

权限数据作为远端状态,登录后拉取并缓存;路由守卫根据权限过滤路由和菜单;按钮/字段级通过高阶组件或 hook 判断;权限变更时重新计算可见性;敏感操作服务端鉴权,前端仅做体验控制。同时要避免权限判断写死在前端。


FB-29-CO-P-028:前端状态管理中的派生状态

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:派生状态、selector、computed、状态 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明派生状态应放在哪里计算,如何避免重复。

参考答案

核心思路如下:

  1. 派生状态可由原始状态计算得出
  2. 计算位置:组件内 useMemo、selector、store computed
  3. 多次使用应缓存,如 Reselect、MobX computed
  4. 避免把派生状态也存入 store 导致同步问题
  5. 复杂派生可在 BFF 计算后返回

需要避免的典型误区:

  • 把派生状态写入 store
  • 每次渲染重新计算
  • 派生逻辑散落在多处

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):派生状态可由原始状态计算得出、计算位置 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 把派生状态写入 store
  • 每次渲染重新计算
  • 派生逻辑散落在多处

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

派生状态可由原始状态计算得出;计算位置;多次使用应缓存,如 Reselect、MobX computed;避免把派生状态也存入 store 导致同步问题;复杂派生可在 BFF 计算后返回。同时要避免把派生状态写入 store。


FB-29-SD-R-033:设计一个支持 Undo/Redo 的前端状态系统

题型:系统设计题 难度:🔴 架构 岗位层级:架构师 / 专家 面试知识域:29 数据与状态 标签:Undo、Redo、状态历史、命令模式、不可变 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请设计一个支持撤销/重做的前端状态系统。

参考答案

核心思路如下:

  1. 使用命令模式:每个操作封装为 do/undo
  2. 维护历史栈:past、present、future
  3. 状态不可变,undo 时恢复上一个快照
  4. 限制历史长度,避免内存无限增长
  5. 支持分支场景:合并、冲突处理

需要避免的典型误区:

  • 每次 setState 都记录历史导致噪音
  • undo 时触发副作用未回滚
  • 可变状态无法正确恢复

补充说明

在实际落地 设计一个支持 Undo/Redo 的前端状态系统 时,建议结合 Undo、Redo、状态历史 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):使用命令模式、维护历史栈 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每次 setState 都记录历史导致噪音
  • undo 时触发副作用未回滚
  • 可变状态无法正确恢复

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

使用命令模式;维护历史栈;状态不可变,undo 时恢复上一个快照;限制历史长度,避免内存无限增长;支持分支场景。同时要避免每次 setState 都记录历史导致噪音。


FB-29-SC-A-003:如何处理前端分页与无限滚动状态

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:分页、无限滚动、状态、缓存、合并 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计列表分页或无限滚动的状态管理方案。

参考答案

核心思路如下:

  1. 分页状态:page、pageSize、total、items
  2. 无限滚动:游标/cursor、hasMore、items
  3. 数据合并:新页数据追加到列表,避免替换
  4. 缓存:按查询条件 key 缓存分页结果
  5. 刷新:下拉刷新重置状态,上拉加载更多

需要避免的典型误区:

  • 每次翻页清空已有数据
  • 页码和游标混用
  • 重复请求同一页

补充说明

在实际落地 处理前端分页与无限滚动状态 时,建议结合 分页、无限滚动、状态 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):分页状态、无限滚动 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每次翻页清空已有数据
  • 页码和游标混用
  • 重复请求同一页

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

分页状态;无限滚动;数据合并;缓存;刷新。同时要避免每次翻页清空已有数据。


FB-29-CO-B-012:useState 与 useReducer 如何选择

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态 标签:React、useState、useReducer、状态 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请说明 useState 和 useReducer 的使用场景。

参考答案

核心思路如下:

  1. useState 适合简单独立状态
  2. useReducer 适合状态相互依赖、转换逻辑复杂
  3. reducer 便于测试和集中管理状态变更
  4. 多个相关状态可用 reducer 统一处理
  5. 复杂表单、多步骤流程适合 useReducer

需要避免的典型误区:

  • 所有状态都用 useReducer
  • useState 中状态更新依赖前一个状态时出错
  • reducer 中做副作用

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):useState 适合简单独立状态、useReducer 适合状态相互依赖、转换逻辑复杂 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有状态都用 useReducer
  • useState 中状态更新依赖前一个状态时出错
  • reducer 中做副作用

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

useState 适合简单独立状态;useReducer 适合状态相互依赖、转换逻辑复杂;reducer 便于测试和集中管理状态变更;多个相关状态可用 reducer 统一处理;复杂表单、多步骤流程适合 useReducer。同时要避免所有状态都用 useReducer。


FB-29-SC-P-004:前端如何处理多标签页状态同步

题型:场景设计题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:多标签页、BroadcastChannel、localStorage、状态同步 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计多标签页间状态同步的方案。

参考答案

核心思路如下:

  1. BroadcastChannel 同源跨标签页通信
  2. localStorage/storage 事件做兼容降级
  3. 同步范围:登录态、主题、语言、购物车
  4. 冲突处理:服务端权威版本、时间戳
  5. 注意隐私和安全,避免同步敏感数据

需要避免的典型误区:

  • 用轮询 polling 同步
  • 所有状态都跨标签同步
  • 忽略 storage 事件只在其他标签触发

补充说明

在实际落地 前端如何处理多标签页状态同步 时,建议结合 多标签页、BroadcastChannel、localStorage 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):BroadcastChannel 同源跨标签页通信、localStorage/storage 事件做兼容降级 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 用轮询 polling 同步
  • 所有状态都跨标签同步
  • 忽略 storage 事件只在其他标签触发

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

BroadcastChannel 同源跨标签页通信;localStorage/storage 事件做兼容降级;同步范围;冲突处理;注意隐私和安全,避免同步敏感数据。同时要避免用轮询 polling 同步。


FB-29-CO-P-029:什么是时间旅行调试

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:时间旅行、Redux DevTools、状态回放、调试 出现频率:低频 预计回答时长:3-5 分钟

题目描述: 请解释时间旅行调试的原理和价值。

参考答案

核心思路如下:

  1. 记录所有状态变更 action 序列
  2. 可以回到任意历史状态查看 UI
  3. 基于不可变状态实现快照回放
  4. 价值:定位 bug、复现问题、理解数据流
  5. Redux DevTools、Vue DevTools 支持

需要避免的典型误区:

  • 可变状态无法回放
  • 副作用导致回放结果不一致
  • 记录所有 action 性能开销大

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):记录所有状态变更 action 序列、可以回到任意历史状态查看 UI 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 可变状态无法回放
  • 副作用导致回放结果不一致
  • 记录所有 action 性能开销大

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

记录所有状态变更 action 序列;可以回到任意历史状态查看 UI;基于不可变状态实现快照回放;价值;Redux DevTools、Vue DevTools 支持。同时要避免可变状态无法回放。


FB-29-SC-R-029:设计一个前端实时协作的状态系统

题型:场景设计题 难度:🔴 架构 岗位层级:架构师 / 专家 面试知识域:29 数据与状态 标签:实时协作、CRDT、OT、状态同步、冲突 出现频率:低频 预计回答时长:15-30 分钟

题目描述: 请设计类似在线文档的实时协作前端状态系统。

参考答案

核心思路如下:

  1. 选择 OT 或 CRDT 作为冲突解决算法
  2. 操作本地先执行,再同步给其他用户
  3. 使用 WebSocket 或 WebRTC 传输操作
  4. 版本向量或 Lamport 时钟定序
  5. 离线支持:本地操作队列,联网后合并
  6. Undo 需考虑远程操作影响

需要避免的典型误区:

  • 直接用最后写入获胜
  • 忽略网络分区
  • 本地状态与远程状态不一致

补充说明

在实际落地 设计一个前端实时协作的状态系统 时,建议结合 实时协作、CRDT、OT 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):选择 OT 或 CRDT 作为冲突解决算法、操作本地先执行,再同步给其他用户 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 直接用最后写入获胜
  • 忽略网络分区
  • 本地状态与远程状态不一致

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

选择 OT 或 CRDT 作为冲突解决算法;操作本地先执行,再同步给其他用户;使用 WebSocket 或 WebRTC 传输操作;版本向量或 Lamport 时钟定序;离线支持;Undo 需考虑远程操作影响。同时要避免直接用最后写入获胜。


FB-29-CO-A-021:Zustand 与 Redux Toolkit 的取舍

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:Zustand、Redux Toolkit、状态管理、轻量 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请比较 Zustand 和 Redux Toolkit,说明各自适合的场景。

参考答案

核心思路如下:

  1. Zustand:轻量、无样板、适合小到中型应用
  2. Redux Toolkit:生态强、DevTools、中间件、适合大型复杂应用
  3. Zustand 支持多 store,Redux 通常单 store
  4. RTK Query 内置数据获取与缓存
  5. 根据团队规模和需求选型

需要避免的典型误区:

  • 大项目用 Zustand 无规范导致混乱
  • 小项目用 Redux 增加样板
  • 频繁跨 store 通信

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):Zustand、Redux Toolkit 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 大项目用 Zustand 无规范导致混乱
  • 小项目用 Redux 增加样板
  • 频繁跨 store 通信

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

Zustand;Redux Toolkit;Zustand 支持多 store,Redux 通常单 store;RTK Query 内置数据获取与缓存;根据团队规模和需求选型。同时要避免大项目用 Zustand 无规范导致混乱。


FB-29-PE-A-002:前端大数据表格的状态与渲染优化

题型:性能优化题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:大数据、表格、状态、虚拟化、分页 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明大数据量表格的状态管理和渲染优化方案。

参考答案

核心思路如下:

  1. 数据分页或滚动加载,避免全量持有
  2. 虚拟滚动只渲染可视行
  3. 单元格编辑状态局部管理
  4. 筛选排序在后端或 Web Worker
  5. 选中状态用 Set 存储行 ID
  6. 避免全表 re-render,使用 memo

需要避免的典型误区:

  • 一次性加载万行数据
  • 表格全局 state 导致所有单元格渲染
  • 编辑状态直接修改原数据

补充说明

在实际落地 前端大数据表格的状态与渲染优化 时,建议结合 大数据、表格、状态 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):数据分页或滚动加载,避免全量持有、虚拟滚动只渲染可视行 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 一次性加载万行数据
  • 表格全局 state 导致所有单元格渲染
  • 编辑状态直接修改原数据

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

数据分页或滚动加载,避免全量持有;虚拟滚动只渲染可视行;单元格编辑状态局部管理;筛选排序在后端或 Web Worker;选中状态用 Set 存储行 ID;避免全表 re-render,使用 memo。同时要避免一次性加载万行数据。


FB-29-CO-P-030:前端状态管理中的副作用处理

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:副作用、异步、Redux Saga、Thunk、数据流 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明前端状态管理中如何处理异步副作用。

参考答案

核心思路如下:

  1. 副作用与纯状态更新分离
  2. Redux Thunk:简单异步逻辑
  3. Redux Saga:复杂流程、竞态处理
  4. RTK Query:封装数据获取、缓存、失效
  5. 组件层:useEffect + 数据层库

需要避免的典型误区:

  • reducer 中直接发请求
  • 不处理竞态和取消
  • 副作用与 UI 强耦合

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):副作用与纯状态更新分离、Redux Thunk 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • reducer 中直接发请求
  • 不处理竞态和取消
  • 副作用与 UI 强耦合

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

副作用与纯状态更新分离;Redux Thunk;Redux Saga;RTK Query;组件层。同时要避免reducer 中直接发请求。


FB-29-SC-A-004:如何设计前端搜索筛选状态

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:搜索、筛选、URL、状态同步、history 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计列表页的搜索筛选状态,支持刷新不丢失和分享。

参考答案

核心思路如下:

  1. 筛选条件同步到 URL query,刷新/分享可恢复
  2. 状态对象与 URL 互相映射
  3. 防抖处理输入,避免频繁请求
  4. 重置按钮清空本地和 URL 状态
  5. 服务端根据 query 返回结果

需要避免的典型误区:

  • 筛选状态只存在内存
  • URL 与状态不同步
  • 每次输入都发请求

补充说明

在实际落地 设计前端搜索筛选状态 时,建议结合 搜索、筛选、URL 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):筛选条件同步到 URL query,刷新/分享可恢复、状态对象与 URL 互相映射 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 筛选状态只存在内存
  • URL 与状态不同步
  • 每次输入都发请求

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

筛选条件同步到 URL query,刷新/分享可恢复;状态对象与 URL 互相映射;防抖处理输入,避免频繁请求;重置按钮清空本地和 URL 状态;服务端根据 query 返回结果。同时要避免筛选状态只存在内存。


FB-29-CO-A-022:什么是不可变数据,为什么重要

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:不可变、Immutable、状态、比较、性能 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请解释不可变数据在前端状态管理中的作用。

参考答案

核心思路如下:

  1. 不可变:创建新对象而不是修改原对象
  2. 优势:可预测、可回放、便于浅比较
  3. React/Vue 依赖不可变或响应式追踪变化
  4. 工具:Immer、Immutable.js、展开运算符
  5. 注意:深层拷贝性能开销,使用结构共享

需要避免的典型误区:

  • 直接修改 state
  • 每次更新都深拷贝导致性能差
  • 认为不可变就是每次都 cloneDeep

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):不可变、优势 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 直接修改 state
  • 每次更新都深拷贝导致性能差
  • 认为不可变就是每次都 cloneDeep

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

不可变;优势;React/Vue 依赖不可变或响应式追踪变化;工具;注意。同时要避免直接修改 state。


FB-29-SD-P-003:设计一个前端配置化表单状态引擎

题型:系统设计题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:表单、配置化、Schema、状态引擎、动态 出现频率:中频 预计回答时长:8-15 分钟

题目描述: 请设计一个通过 JSON Schema 驱动表单渲染和状态管理的引擎。

参考答案

核心思路如下:

  1. 用 JSON Schema 统一描述字段:类型、默认值、校验规则、联动条件与布局权重,避免字段定义散落在 UI 组件里。
  2. 状态引擎按字段路径(如 user.profile.name)构建嵌套状态树,支持数组、对象与动态增删;变更通过不可变更新触发订阅。
  3. 联动层采用依赖图或事件总线:当字段 A 变化时,根据规则重新计算字段 B 的显隐、可编辑态、候选值与校验规则。
  4. 校验分层执行:输入即时校验、失焦校验、提交前全量校验,并支持异步服务端校验;错误信息按字段路径回写。
  5. 渲染层通过组件映射表将 Schema 映射为具体表单控件,支持自定义组件、自定义校验器和自定义布局注册。
  6. 性能与可扩展:大数据表单启用虚拟滚动/分片渲染,联动计算使用 memoization;提供插件机制与运行时调试面板。

需要避免的典型误区:

  • Schema 与组件强耦合
  • 联动逻辑复杂难以调试
  • 所有字段都全局状态

补充说明

在实际落地 设计一个前端配置化表单状态引擎 时,建议结合 表单、配置化、Schema 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):Schema 描述字段、类型、校验、联动、布局、状态树按字段路径组织,支持嵌套 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • Schema 与组件强耦合
  • 联动逻辑复杂难以调试
  • 所有字段都全局状态

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

Schema 描述字段、类型、校验、联动、布局;状态树按字段路径组织,支持嵌套;联动规则;校验分层;扩展。同时要避免Schema 与组件强耦合。


FB-29-SC-A-005:前端购物车状态如何设计

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:购物车、状态、持久化、同步、乐观更新 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计电商前端购物车的状态管理方案。

参考答案

核心思路如下:

  1. 本地状态:商品列表、数量、选中、价格计算
  2. 持久化:localStorage/IndexedDB,登录后同步到服务端
  3. 数量加减可乐观更新,失败回滚
  4. 价格等敏感信息以服务端为准
  5. 未登录与登录状态平滑迁移

需要避免的典型误区:

  • 所有购物车逻辑放全局
  • 前端计算总价作为结算依据
  • 离线添加商品丢失

补充说明

在实际落地 前端购物车状态如何设计 时,建议结合 购物车、状态、持久化 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):本地状态、持久化 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 所有购物车逻辑放全局
  • 前端计算总价作为结算依据
  • 离线添加商品丢失

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

本地状态;持久化;数量加减可乐观更新,失败回滚;价格等敏感信息以服务端为准;未登录与登录状态平滑迁移。同时要避免所有购物车逻辑放全局。


FB-29-CO-P-031:状态管理中的selector模式

题型:概念题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:selector、Reselect、派生状态、缓存 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明 selector 模式的作用和最佳实践。

参考答案

核心思路如下:

  1. selector 从 store 中派生数据
  2. 可组合:简单 selector 组合成复杂 selector
  3. 缓存:输入不变输出不变,避免重复计算
  4. 解耦组件与 store 结构
  5. Reselect、Zustand selector、Vue getter 都是实现

需要避免的典型误区:

  • 在组件里直接遍历 store
  • selector 返回新对象导致渲染
  • 所有派生逻辑写在 reducer

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):selector 从 store 中派生数据、可组合 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 在组件里直接遍历 store
  • selector 返回新对象导致渲染
  • 所有派生逻辑写在 reducer

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

selector 从 store 中派生数据;可组合;缓存;解耦组件与 store 结构;Reselect、Zustand selector、Vue getter 都是实现。同时要避免在组件里直接遍历 store。


FB-29-FS-A-001:状态更新后 UI 未更新如何排查

题型:故障排查题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:状态、UI、渲染、调试、React 出现频率:高频 预计回答时长:5-8 分钟

题目描述: 请给出状态更新后组件未重新渲染的排查思路。

参考答案

核心思路如下:

  1. 确认状态确实已更新,检查 DevTools
  2. 检查是否直接修改了原状态
  3. 检查 selector 是否返回相同引用
  4. 检查组件是否被 memo 且 props 未变
  5. 检查状态是否更新到了正确的作用域
  6. 使用日志或时间旅行回放定位

需要避免的典型误区:

  • 直接修改 state 还以为会更新
  • 忽略浅比较
  • 在不相关组件里查找

补充说明

在实际落地 状态更新后 UI 未更新如何排查 时,建议结合 状态、UI、渲染 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):确认状态确实已更新,检查 DevTools、检查是否直接修改了原状态 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 直接修改 state 还以为会更新
  • 忽略浅比较
  • 在不相关组件里查找

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

确认状态确实已更新,检查 DevTools;检查是否直接修改了原状态;检查 selector 是否返回相同引用;检查组件是否被 memo 且 props 未变;检查状态是否更新到了正确的作用域;使用日志或时间旅行回放定位。同时要避免直接修改 state 还以为会更新。


FB-29-CO-B-013:什么是单向数据流

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态 标签:单向数据流、Flux、Redux、Vuex、可预测 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请解释单向数据流的概念和优势。

参考答案

核心思路如下:

  1. 数据只能从父到子、从 state 到 view,通过 action 改变 state
  2. 代表:Flux、Redux、Vuex
  3. 优势:数据流向清晰、易于追踪、调试
  4. 与双向绑定对比:更可控但代码更多
  5. 现代框架推荐单向数据流为主

需要避免的典型误区:

  • 子组件直接修改父组件状态
  • 双向绑定滥用导致数据流混乱
  • 认为单向数据流性能更好

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):数据只能从父到子、从 state 到 view,通过 action 改变 state、代表 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 子组件直接修改父组件状态
  • 双向绑定滥用导致数据流混乱
  • 认为单向数据流性能更好

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

数据只能从父到子、从 state 到 view,通过 action 改变 state;代表;优势;与双向绑定对比;现代框架推荐单向数据流为主。同时要避免子组件直接修改父组件状态。


FB-29-SC-P-005:前端如何处理服务端推送状态

题型:场景设计题 难度:🟣 深入 岗位层级:高级 / 资深 面试知识域:29 数据与状态 标签:SSE、WebSocket、推送、状态更新、实时 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计服务端推送数据的前端状态处理方案。

参考答案

核心思路如下:

  1. 根据场景选 SSE 或 WebSocket
  2. 连接管理:重连、心跳、断线提示
  3. 推送事件映射到 store action 更新状态
  4. 消息去重与顺序处理
  5. 避免高频消息导致频繁渲染:节流/批量

需要避免的典型误区:

  • 每条消息都触发全局渲染
  • 断线不重连
  • 不处理消息乱序

补充说明

在实际落地 前端如何处理服务端推送状态 时,建议结合 SSE、WebSocket、推送 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):根据场景选 SSE 或 WebSocket、连接管理 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每条消息都触发全局渲染
  • 断线不重连
  • 不处理消息乱序

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

根据场景选 SSE 或 WebSocket;连接管理;推送事件映射到 store action 更新状态;消息去重与顺序处理;避免高频消息导致频繁渲染。同时要避免每条消息都触发全局渲染。


FB-29-SD-R-034:设计一个跨页面共享的状态层

题型:系统设计题 难度:🔴 架构 岗位层级:架构师 / 专家 面试知识域:29 数据与状态 标签:跨页面、状态共享、Storage、BroadcastChannel、同步 出现频率:低频 预计回答时长:15-30 分钟

题目描述: 请设计一个支持多页面/子应用共享的状态层。

参考答案

核心思路如下:

  1. 核心状态抽象为可序列化对象
  2. 存储介质:IndexedDB/localStorage/BroadcastChannel
  3. 状态变更走统一 API,自动同步到存储和其他页面
  4. 冲突解决策略:时间戳、版本号、服务端仲裁
  5. 权限与隐私:敏感状态不共享
  6. API 设计:订阅、读取、写入、清除

需要避免的典型误区:

  • 直接暴露存储对象
  • 各页面状态版本不一致
  • 忽略存储容量限制

补充说明

在实际落地 设计一个跨页面共享的状态层 时,建议结合 跨页面、状态共享、Storage 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):核心状态抽象为可序列化对象、存储介质 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 直接暴露存储对象
  • 各页面状态版本不一致
  • 忽略存储容量限制

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

核心状态抽象为可序列化对象;存储介质;状态变更走统一 API,自动同步到存储和其他页面;冲突解决策略;权限与隐私;API 设计。同时要避免直接暴露存储对象。


FB-29-CO-A-023:状态管理中的副作用隔离

题型:概念题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:副作用、状态、reducer、异步、可预测 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明如何在状态管理中隔离副作用。

参考答案

核心思路如下:

  1. reducer 保持纯函数,不直接发起请求
  2. 副作用集中在 middleware/saga/effect
  3. 组件层使用自定义 hook 封装副作用
  4. 异步取消和竞态处理
  5. 测试时副作用可 mock

需要避免的典型误区:

  • reducer 中直接 fetch
  • 副作用散落在所有组件
  • 不处理竞态

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):reducer 保持纯函数,不直接发起请求、副作用集中在 middleware/saga/effect 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • reducer 中直接 fetch
  • 副作用散落在所有组件
  • 不处理竞态

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

reducer 保持纯函数,不直接发起请求;副作用集中在 middleware/saga/effect;组件层使用自定义 hook 封装副作用;异步取消和竞态处理;测试时副作用可 mock。同时要避免reducer 中直接 fetch。


FB-29-SC-A-006:如何设计前端搜索状态与历史记录

题型:场景设计题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:搜索、历史、状态、URL、撤销 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请设计搜索框的输入状态、历史记录和结果缓存。

参考答案

核心思路如下:

  1. 输入状态用受控组件 + 防抖
  2. 历史记录持久化到本地存储
  3. 最近搜索支持删除和快捷填充
  4. 搜索条件同步到 URL
  5. 结果按条件 key 缓存

需要避免的典型误区:

  • 每次输入都请求
  • 历史记录无上限
  • 缓存 key 不包含所有条件

补充说明

在实际落地 设计前端搜索状态与历史记录 时,建议结合 搜索、历史、状态 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):输入状态用受控组件 + 防抖、历史记录持久化到本地存储 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 每次输入都请求
  • 历史记录无上限
  • 缓存 key 不包含所有条件

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

输入状态用受控组件 + 防抖;历史记录持久化到本地存储;最近搜索支持删除和快捷填充;搜索条件同步到 URL;结果按条件 key 缓存。同时要避免每次输入都请求。


FB-29-CO-B-014:什么是状态提升(Lifting State Up)

题型:概念题 难度:🟢 基础 岗位层级:初级 / 高级 面试知识域:29 数据与状态 标签:状态提升、React、共享、props、父子 出现频率:高频 预计回答时长:3-5 分钟

题目描述: 请解释状态提升的概念和使用场景。

参考答案

核心思路如下:

  1. 多个子组件需要共享状态时,把状态放到共同父组件
  2. 子组件通过 props 接收值和回调
  3. 适合层级不深、共享范围小的场景
  4. 过深时用 Context 或状态管理库
  5. 保持单向数据流

需要避免的典型误区:

  • 状态提升过高导致 prop drilling
  • 所有状态都提升
  • 在深层子组件中反向修改

评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):多个子组件需要共享状态时,把状态放到共同父组件、子组件通过 props 接收值和回调 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 状态提升过高导致 prop drilling
  • 所有状态都提升
  • 在深层子组件中反向修改

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

多个子组件需要共享状态时,把状态放到共同父组件;子组件通过 props 接收值和回调;适合层级不深、共享范围小的场景;过深时用 Context 或状态管理库;保持单向数据流。同时要避免状态提升过高导致 prop drilling。


FB-29-PE-A-003:状态更新批处理与性能

题型:性能优化题 难度:🔵 进阶 岗位层级:中级 / 高级 面试知识域:29 数据与状态 标签:批处理、setState、调度、React、性能 出现频率:中频 预计回答时长:5-8 分钟

题目描述: 请说明状态更新批处理如何影响性能。

参考答案

核心思路如下:

  1. React 18 自动批处理多个 setState
  2. 减少渲染次数,提升性能
  3. flushSync 可强制同步更新但慎用
  4. 在事件处理、异步代码中行为不同
  5. 合并相关状态更新

需要避免的典型误区:

  • 手动合并所有 setState
  • 滥用 flushSync
  • 不理解自动批处理边界

补充说明

在实际落地 状态更新批处理与性能 时,建议结合 批处理、setState、调度 的真实场景做验证。重点关注可观测性埋点、异常降级路径和性能基线回归;同时通过灰度发布、指标看板和复盘机制持续迭代,确保方案从“能跑”演进为“可维护、可扩展”。 评分维度

  • 问题理解与分析(35%):是否抓住核心矛盾
  • 方案设计(45%):React 18 自动批处理多个 setState、减少渲染次数,提升性能 等关键点
  • 落地与优化(20%):是否考虑监控、回滚、演进

常见错误

  • 手动合并所有 setState
  • 滥用 flushSync
  • 不理解自动批处理边界

延伸追问

  • 如果候选人答对了,可追问:能否举一个你实际项目中遇到的具体例子,并说明当时的取舍?
  • 如果候选人答错了,可引导:如果从“稳定性、可维护性、性能”三个维度再看,你还会补充哪些点?

相关题目

  • 暂无

参考资源

口头回答版

React 18 自动批处理多个 setState;减少渲染次数,提升性能;flushSync 可强制同步更新但慎用;在事件处理、异步代码中行为不同;合并相关状态更新。同时要避免手动合并所有 setState。


基于 MIT 协议发布