首页/文章/八股文

React进阶篇前端面试题

2025-07-20
4642 分钟
...

一、核心机制

1.合成事件机制

1.设计目的与核心原理

1.跨浏览器一致性
  • 统一不同浏览器的事件处理接口(如event.target的行为)
  • 修复浏览器兼容性问题(如IE事件模型差异)
2.性能优化
  • 事件委托:将事件绑定到根节点(React 17+ 为应用根DOM),而非每个子元素
  • 事件池化Event Pooling):复用事件对象,减少内存开销
3.扩展能力
  • 支持自定义事件类型(如 onDoubleClick
  • 实现高级功能(如事件优先级调度)

2.事件委托机制演进

| ​React 版本​ | ​委托层级​ | ​核心变化​ | | --- | --- | --- | --- | | 16.x 及之前 | 所有事件委托到 document | 多 React 应用共存时事件可能冲突 | | 17.x 及之后 | 委托到应用根 DOM 节点 | 隔离不同 React 版本的事件系统,避免全局污染 | ```js |

const rootNode = document.getElementById(‘root’); ReactDOM.render(, rootNode);

#### 3.合成事件对象

##### 1.核心属性

```js
interface SyntheticEvent {
  nativeEvent: Event;          
  currentTarget: DOMElement;  
  target: DOMElement;         
  type: string;               
  isDefaultPrevented(): boolean;
  isPropagationStopped(): boolean;
  persist(): void;           
}
2.事件池化示例
function handleClick(event) {
  
  setTimeout(() => {
    console.log(event.target); 
  }, 100);
  
  
  event.persist();
  setTimeout(() => {
    console.log(event.target); 
  }, 100);
}

4.事件处理流程

1.事件注册
  • React初始化时注册所有支持的事件(如onClick,onChange
  • 通过EventListener在根节点监听原生事件
2.事件触发
原生事件触发根节点捕获事件React 生成 SyntheticEvent收集事件监听器按组件树冒泡/捕获顺序执行
3.执行顺序
  • 捕获阶段父组件 onClickCapture -> 子组件 onClickCapture
  • 冒泡阶段子组件 onClick -> 父组件 onClick

与原生事件交互

1.混合使用场景
useEffect(() => {
  const handleNativeClick = (e) => {
    console.log('原生事件触发');
  };
  document.addEventListener('click', handleNativeClick);

  return () => {
    document.removeEventListener('click', handleNativeClick);
  };
}, []);


const handleReactClick = (e) => {
  console.log('合成事件触发');
  e.stopPropagation(); 
};
2.执行顺序
原生事件捕获) → 原生事件目标) → React 事件捕获) → React 事件目标) → React 事件冒泡) → 原生事件冒泡

6.常见问题与解决方案

1.事件阻止传播失败
  • 问题:e.stopPropagation() 仅阻止React事件传播,不影响原生事件
  • 方案:同时阻止原生事件传播
const handleClick = (e) => {
  e.stopPropagation();
  e.nativeEvent.stopImmediatePropagation();
};
2.事件监听器性能优化
  • 避免在渲染时创建新函数:使用useCallback函数缓存
const handleClick = useCallback((e) => {  }, []);

7.高频面试题

1.为什么React不直接将事件绑定在元素上?
  • 事件委托减少内存占用,动态更新组件时无需重新绑定事件
2.合成事件和原生事件的区别
  • 合成事件跨浏览器统一行为
  • 原生事件直接操作DOM,无React抽象层
3.如何全局阻止React事件
  • 劫持根节点事件监听
document.getElementById('root').addEventListener('click', e => {
  e.stopImmediatePropagation();
}, true);

2.组件更新触发条件与渲染优化

1.组件更新触发条件

组件重新渲染的根本原因是组件状态或数据依赖发生变化,具体触发场景如下:

触发条件说明
State 变化组件内部 useState/useReducer/this.setState 更新状态
Props 变化父组件重新渲染导致传入的 props 值变化
Context 更新组件订阅的 Context 数据发生变更
父组件重新渲染即使子组件的 props 未变化,父组件渲染仍可能导致子组件重新渲染(默认行为)
强制更新类组件调用 this.forceUpdate()
Hooks 依赖变化useEffect/useMemo/useCallback 的依赖数组元素变更

2.React渲染机制核心原理

1.渲染流程
  • 触发更新 -> 生成虚拟DOM -> Diff算法比较 -> 确定DOM更新范围 -> 提交到真实DOM
2.协调(Reconciliation)策略
  • 树对比:仅对比同层级节点,时间复杂度O(n)
  • Key值优化:列表项使用key帮助React识别元素移动/复用

3.渲染优化策略与实践

1.避免不必要的父组件渲染
  • 场景:父组件状态变化导致所有子组件重新渲染
  • 优化方案:
function Parent() {
  return (
    <>
      <ExpensiveChild />
      <StateContainer /> // 将易变状态抽离
    </>
  );
}
2.组件自身渲染控制
  • 类组件
class MyComponent extends React.PureComponent { 
  shouldComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps); 
  }
}
  • 函数组件
const MemoizedComponent = React.memo(MyComponent, (prevProps, nextProps) => {
  return prevProps.id === nextProps.id; 
});
3.精细化Hooks使用
  • 缓存计算结果
const expensiveValue = useMemo(() => computeValue(a, b), [a, b]);
  • 稳定函数引用
const handleClick = useCallback(() => {
  
}, [a]);
  • 按需订阅Context
const value = useContextSelector(MyContext, v => v.requiredField);
4.列表渲染优化
  • 虚拟滚动:使用react-windowreact-virtualized
import { FixedSizeList as List } from 'react-window';

<List height={600} itemSize={35} itemCount={1000}>
  {({ index, style }) => <div style={style}>Row {index}</div>}
</List>
  • key值策略
{items.map((item, index) => <Item key={index} />)}


{items.map(item => <Item key={item.id} />)}

4.高频面试题

1.为什么父组件更新会导致所有子组件渲染?如何避免?
  • react默认采用"render and diff"策略,使用React.memo/shouldComponentUpdate阻断无效更新
2.useMemo一定能提升性能吗?使用场景是什么?
  • 不一定。仅当计算开销大且稳定时使用,否则可能因依赖数组计算反增开销
3.如何优化Context引起的渲染?
  • 拆分多个Context
  • 使用useContextSelector按需订阅
const ThemeButton = () => {
  const theme = useContextSelector(ThemeContext, v => v.color);
  return <button style={{ color: theme }}>Submit</button>;
};
4.函数组件每次渲染都会创建新函数,如何避免传递新props?
  • 使用useCallback缓存函数引用
const handleSubmit = useCallback(() => {  }, [deps]);

6.性能优化法则

  1. 优先解决重复渲染问题:使用React DevTools Profiler定位关键路径
  2. 避免过早优化:只在性能瓶颈出现时实施优化
  3. 保持组件纯净:减少渲染过程中的副作用操作
  4. 控制渲染范围:使用children props阻断无关更新
<Layout>
  <StaticContent /> {}
</Layout>


function Layout({ children }) {
  const [state, setState] = useState();
  return <div>{children}</div>;
}

3.Hooks核心原理

1.Hooks的设计目标

  1. 逻辑复用:解决类组件中高阶组件(HOC)和Render Props的嵌套地狱问题
  2. 简化组件:告别this绑定和生命周期方法的分散逻辑
  3. 函数式优先:拥抱函数式编程范式,提升代码可预测性
  4. 渐进式升级:兼容现有组件,无需重写即可逐步迁移

2.核心原理

1.闭包与链表存储
  • 存储结构:Hooks数据存储在Fiber节点的memoizedState属性中,通过单向链表管理
  • 执行顺序依赖:Hooks调用顺序在每次渲染中必须严格一致(链表顺序不可变)
  • 闭包陷阱:每个Hooks闭包捕获当次渲染的props/state快照
const fiber = {
  memoizedState: {
    memoizedState: '状态值',    
    next: {                   
      memoizedState: [],      
      next: null
    }
  }
};
2.调度机制
  • 优先级调度:Hooks更新请求会被Scheduler模块根据优先级(Immediate/UserBlocking/Normal)排队处理
  • 批量更新:React自动合并多个setState调用,减少渲染次数

3.核心Hooks原理解析

1.useState
  • 存储结构[state, dispatchAction]存储在链表节点中
  • 更新触发:调用dispatchAction会创建更新对象,触发重新渲染
  • 异步更新:状态更新在下次渲染时生效(遵循批量更新原则)
function useState(initial){
    const fiber = getCurrentFiber();
    const hook = fiber.memorizedState?.isStateHook ? fiber.memoizedState : { memouzedState: initial, next: null };
    const dispatch = (action) => {
        const update = { action };
        hook.queue.push(update);
        scheduleWork(); 
    };
    return [hook.memoizedState, dispatch];
}
2.useEffect
  • 依赖对比:使用浅比较(Object.is)判断依赖数组变化
  • 执行时机:在浏览器完成布局与绘制后异步执行(避免阻塞渲染)
  • 清理机制:返回的清理函数会在下次effect执行前或组件卸载前执行
function areDepsEqual(prevDeps, nextDeps) {
  if (!prevDeps || !nextDeps) return false;
  for (let i = 0; i < prevDeps.length; i++) {
    if (!Object.is(prevDeps[i], nextDeps[i])) return false;
  }
  return true;
}
3.useRef
  • 跨渲染存储:ref对象在组件生命周期内保持不变
  • 直接修改ref.current的修改不会触发重新渲染
function useRef(initialValue) {
  const ref = { current: initialValue };
  return useMemo(() => ref, []); 
}

4.Hooks规则的本质

1.为什么必须顶层调用?
  • 链表顺序依赖:Hooks的存储依赖调用顺序,条件语句会破坏链表结构
2.为什么只能在函数组件中使用?
  • Fiber关联:Hooks需要绑定当前组件的Fiber节点,普通函数无此上下文

5.闭包陷阱与解决方案

1.过期闭包问题
function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1); 
    }, 1000);
    return () => clearInterval(timer);
  }, []); 

  
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(c => c + 1); 
    }, 1000);
    return () => clearInterval(timer);
  }, []);
}
2.解决方案
  • 函数式更新setState(c => c + 1)
  • 依赖数组精准化:确保所有依赖项被正确声明
  • useRef穿透闭包:通过ref访问最新值
const countRef = useRef(count);
countRef.current = count;

6.高频面试题

1.Hooks如何实现状态隔离?
  • 每个组件实例的Fiber节点维护独立的Hooks链表,相同组件不同实例互不影响
2.自定义Hook的本质是什么?
  • 将多个内置Hooks组合为可复用的逻辑单元,遵循Hooks规则
3.为什么useEffect的依赖数组是浅比较
  • 性能优化考量,深比较成本过高。复杂对象应使用useMemo稳定引用
4.useMemo/useCallback如何避免重复计算?
  • 通过依赖数组决定是否重新计算,引用稳定时返回缓存值
const memoValue = useMemo(() => compute(a), [a]);
const memoFn = useCallback(() => action(a), [a]);

7.性能优化策略

优化手段实现方式适用场景
精细化依赖数组确保依赖数组只包含必要变量所有 Hooks
状态提升将状态提升到父组件或 Context多组件共享状态
惰性初始 stateuseState(() => expensiveInit())初始值计算成本高时
批量更新unstable_batchedUpdates(React 18 自动)合并多次状态更新

4.常用Hooks

1.基础Hooks

1.useState
  • 作用:为函数组件添加状态管理能力
  • 使用场景:组件内部的状态管理
const [count, setCount] = useState(() => 0); 
const increment = () => setCount(prev => prev + 1);
  • 注意
    • 状态更新是异步的,连续调用会被合并
    • 复杂对象建议使用useReducer
2.useEffect
  • 作用:处理副作用(数据请求、DOM操作、订阅)
  • 生命周期映射
    • componentDidMount -> 依赖数组为空 []
    • componentDidUpdate -> 指定依赖项 [dep]
    • componentWillUnmount -> 返回清理函数
useEffect(() => {
  const subscription = props.source.subscribe();
  return () => subscription.unsubscribe(); 
}, [props.source]);
  • 注意
    • 默认在浏览器完成渲染后异步执行
    • 使用useLayoutEffect处理同步DOM操作
3.useContext
  • 作用:跨组件层级传递数据
const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div>{theme}</div>;
}
  • 优化:使用memo防止无关组件更新

2.性能优化Hooks

1.useMemo
  • 作用:缓存计算结果,避免重复计算
const expensiveValue = useMemo(() => compute(a, b), [a, b]);
  • 注意
    • 依赖数组引用,避免子组件无效渲染
    • 不应用于有副作用的操作
2.useCallback
  • 作用:缓存函数引用,避免子组件无效渲染
const handleSubmit = useCallback(() => {
  submitData(name);
}, [name]);
  • 等价写法
const memoizedFn = useMemo(() => () => submitData(name), [name]);
3.React.memo
  • 作用:浅比较props变化,阻止无效渲染
const MemoComponent = React.memo(Child, (prev, next) => {
  return prev.id === next.id; 
});

进阶Hooks

1.useReducer
  • 作用:复杂状态逻辑管理(类似Redux)
const initialState = { count: 0 };
function reducer(state, action) {
    switch(action.type) {
        case 'increment':
            return { count: state.count + 1 };
        default: return state;
    }
}
const [count, dispatch] = useReducer(reducer, initialState);
  • 优势:适合多状态联动或逻辑状态复杂的场景
2.useRef
  • 作用:
    • 访问DOM元素
    • 保存可变值(不触发渲染)
const inputRef = useRef();
useEffect(() => inputRef.current.focus(), []);


const prevCount = useRef(count);
useEffect(() => {
  prevCount.current = count;
});
3.useLayoutEffect
  • 作用:同步执行副作用,在浏览器绘制前完成DOM修改
  • 场景:测量DOM布局、同步样式调整
useLayoutEffect(() => {
  const width = divRef.current.offsetWidth;
  setWidth(width); 
}, []);

4.特殊场景Hooks

1.useImperativeHandle
  • 作用:自定义暴露给父组件的实例方法
const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus()
  }));
  return <input ref={inputRef} />;
});
2.useDebugValue
  • 作用:在React开发中工具中显示自定义hook标签
function useFriendStatus() {
  const [isOnline] = useState(null);
  useDebugValue(isOnline ? 'Online' : 'Offline');
  return isOnline;
}

5.高频面试题

1.useEffect和useLayoutEffect的区别?
  • useEffect异步执行(不阻塞渲染)
  • useLayoutEffect同步执行(在DOM更新后,浏览器绘制前)
2.如何避免useEffect的无限循环?
  • 正确设置依赖数组
  • 使用useCallback/useMemo稳定引用
3.useMemo和React.memo的区别?
  • useMemo缓存值
  • React.memo缓存组件渲染结果

5.虚拟DOM Diff算法原理

1.核心设计思想

React的Diff算法基于两个核心假设,以O(n)时间复杂度完成树结构的高效比较

  1. 类型差异假设:不同类型的元素会生成不同的树结构,直接替换整个子树
  2. Key稳定性假设:通过key标识相同层级的元素是否可复用

2.分层对比策略

React采用逐层递归比较,但不会跨层级追踪节点变化

旧树<div>          新树<section>
        <A/>               <A/>
        <B/>               <B/>
      </div>            </section>

处理逻辑发现 divsection 类型不同直接销毁整个 div 子树重建 section 子树

3.同层级节点比较

当父节点类型相同时,递归比较其他子节点

1.列表节点对比优化
  • 场景:动态列表项的顺序变化(增删、排序)
  • 策略:使用唯一的key标识元素身份,最小化移动操作
<ul>
  <li key="a">A</li>
  <li key="b">B</li>
</ul>


<ul>
  <li key="b">B</li>
  <li key="a">A</li>
</ul>


2.无key列表的默认行为
  • 使用索引作为key:可能导致错误复用(如列表项内容变化但位置不变)
  • 性能陷阱:列表顺序变化时,索引变化导致不必要的重新渲染

4.元素类型处理

1.类型相同
  • 更新属性:仅修改变化的属性(如className、style)
  • 递归比较子节点:继续对比子元素
2.类型不同
  • 销毁旧子树:触发旧组件 componentWillUnmount
  • 创建新子树:触发新组件 constructor -> render -> componentDidMount

5.Diff算法步骤分解

1.根节点对比
  • 类型不同 -> 整树替换
  • 类型相同 -> 进入属性比较
2.属性更新
function updateDOMProperties(domElement, prevProps, nextProps) {
  
  Object.keys(prevProps).forEach(propName => {
    if (!nextProps.hasOwnProperty(propName)) {
      domElement.removeAttribute(propName);
    }
  });
  
  
  Object.keys(nextProps).forEach(propName => {
    if (prevProps[propName] !== nextProps[propName]) {
      domElement.setAttribute(propName, nextProps[propName]);
    }
  });
}
3.子节点递归对比
  • 策略:双指针遍历新旧子节点列表
  • 操作类型
    • INSERT:新增节点
    • MOVE:移动已有节点
    • REMOVE:删除废旧节点

key的优化机制

场景无 Key有 Key
列表项顺序变化索引变化导致全部重新渲染识别移动项,仅调整 DOM 顺序
列表中间插入项后续项索引变化触发多节点更新仅插入新节点,后续项无变化
删除中间项后续项索引变化触发多节点更新仅删除目标节点

7.性能优化实践

1.key的使用原则
场景无 Key有 Key
列表项顺序变化索引变化导致全部重新渲染识别移动项,仅调整 DOM 顺序
列表中间插入项后续项索引变化触发多节点更新仅插入新节点,后续项无变化
删除中间项后续项索引变化触发多节点更新仅删除目标节点

7.性能优化实践

1.key的使用原则
  • 使用数据唯一的标识(如id)
  • 避免随机数或索引(不稳定)
2.避免跨层级移动节点
  • 修改父节点类型会导致子树重建
3.减少顶层节点类型变化
  • 保持稳定的组件结构
4.复杂列表优化
  • 虚拟滚动(如reac-window)
  • 分页加载

高频面试题

1.为什么列表必须使用key?
  • 帮助React识别元素身份,在顺序变化时高效复用DOM节点,避免以下问题:
    • 不必要的子组件重新渲染
    • 表单元素状态错乱(如输入框内容错位)
2.如何强制组件重新挂载?
  • 改变key值触发销毁/重建
<UserProfile key={user.id} user={user} />
3.Diff算法能完全避免DOM操作吗?
  • 不能。目的是最小化操作次数,但无法消除必要的更新(如数据变化必然导致DOM修改)

6.Fiber

1.Fiber的诞生背景

1.旧版协调算法瓶颈
  • 递归不可中断:同步遍历整个虚拟DOM树,长时间占用主线程
  • 卡顿问题:复杂组件树更新导致掉帧(如大型列表、动画场景)
2.目标
  • 实现增量渲染,支持异步可中断的更新

2.Fiber核心设计思想

1.时间切片
  • 将渲染任务拆分为多个小任务(Fiber节点)
  • 利用浏览器空闲时段(requestIdleCallback)分片执行
2.优先级调度
  • 用户交互(如输入)优先于数据更新(如API响应)
3.可恢复工作单元
  • 保存中间状态,允许暂停/恢复渲染流程

3.Fiber节点数据结构

每个Fiber节点对应一个组件实例或DOM节点,包含以下核心属性:

属性类型作用
typeString/Object组件类型(如 'div'、函数组件引用)
stateNodeObject对应的 DOM 节点或类组件实例
childFiber第一个子节点
siblingFiber下一个兄弟节点
returnFiber父节点
pendingPropsObject新传入的 props
memoizedPropsObject上一次渲染使用的 props
memoizedStateObject上一次渲染后的 state(如 Hooks 链表)
effectTagNumber标记副作用类型(如 PlacementUpdateDeletion
alternateFiber指向当前 Fiber 的镜像(用于 Diff 比较)

4.Fiber双缓冲机制

React维护两颗Fiber树已确保更新无冲突

  1. Current Tree:当前已渲染的UI对应的Fiber树
  2. WorkInProgress Tree:正在构建的新Fiber树
  • 切换流程:更新完成后,WorkInProgress Tree树变为Current树
初始渲染
Current Tree: null
WorkInProgress Tree: → 构建完成提交后成为 Current Tree

更新阶段
Current Tree ←→ WorkInProgress Tree复用或新建节点

3.Fiber渲染

1.协调阶段
  • 目标:生成副作用列表,不修改DOM
  • 过程
    1. 递阶段:调用render生成子节点,标记变化
    2. 归阶段:向上回溯收集副作用
  • 可中断:根据剩余时间片暂停/恢复遍历
2.提交阶段
  • 目标:同步执行所有DOM变更
  • 过程
    1. Before Mutation:调用getSnapshotBeforeUpdate
    2. Mutation:执行DOM增删改
    3. Layout:调用useLayoutEffectcomponentEDidMount/Update
  • 不可中断:避免中间状态导致UI不一致

6.优先级调度模型

​优先级​​对应场景​
ImmediatePriority同步任务(如 flushSync
UserBlockingPriority用户交互(点击、输入)
NormalPriority数据更新、网络响应
LowPriority过渡更新(Concurrent 模式)
IdlePriority空闲时执行的任务
  • 调度策略:高优先级任务可中断低优先级任务

7.Fiber对生命周期的影响

1.废弃生命周期:
  • componentWillMount、componentWillReceiveProps、componentWillUpdate
  • 原因:异步可渲染可能导致多次调用,引发副作用错误
2.新增API
  • getDerivedStateFromProps(静态方法,替代componentWillReceiveProps
  • getSnapshotBeforeUpdate(替代componentWillUpdate

8.Fiber与并发模式

1.并发特性
  • useTranstion:标记非紧急更新,可被高优先级任务打断
  • Suspense:等待异步数据加载时显示回退UI
2.启用方式
ReactDOM.createRoot(document.getElementById('root')).render(<App />);

9.高频面试题

1.Fiber如何实现可中断更新?
  • 通过链表结构存储Fiber节点,记录遍历进度。每次处理一个Fiber后检查剩余时间片,不足时保存当前进度,将控制权交还浏览器
2.协调阶段和提交阶段的区别
  • 协调阶段负责计算变更(可中断),提交阶段执行DOM操作(同步不可中断)
3.为什么需要双缓冲机制?
  • 保证渲染过程中Current Tree始终完整可用,避免更新过程中出现UI不一致

二、状态管理

1.状态管理的核心

  1. 组件通信:跨层级组件共享数据
  2. 状态同步:多个组件依赖统一数据源
  3. 副作用管理:异步操作(API请求、定时器)与状态更新的协调
  4. 调试追踪:复杂应用中状态变更的可预测性

2.React内置解决方案

1.组件状态(useState/useReducer)

  • 适用场景:组件内部私有状态(如表单输入、UI切换)
const [count, setCount] = useState(0);


const reducer = (state, action) => {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    default: return state;
  }
};
const [state, dispatch] = useReducer(reducer, { count: 0 });

2.Context API

  • 适用场景:中低频次更新的全局状态(如主题、用户身份)
const ThemeContext = createContext();
const UserContext = createContext();

const UserPanel = memo(() => {
    const user = useContext(UserContext);
    return <div>{user.name}</div>;
})

3.主流状态管理库对比

​方案​​核心模式​​优点​​缺点​​适用场景​
​Redux​单一 Store + Flux严格的数据流、强大的中间件生态样板代码多、学习曲线陡峭大型复杂应用、需要时间旅行调试
​MobX​响应式编程代码简洁、自动追踪依赖黑盒机制、过度渲染风险中小型应用、快速开发
​Recoil​原子状态 + Selector细粒度控制、原生支持异步较新、生态不成熟需要局部状态优化的场景
​Zustand​轻量 StoreAPI 简单、无样板代码功能相对基础中小项目、替代部分 Redux 用例
​Context + Hooks​组合式状态零依赖、React 原生支持性能问题、缺乏中间件简单全局状态管理

4.Redux核心原理与最佳实践

1.三大原则:

  • 单一数据源:整个应用状态存储在一个Store中
  • 只读State:通过Action修改意图
  • 纯函数Reducer:接收旧State和Action,返回新State

2.现代Redux开发(Redux Toolkit)

const counterSlice = createSlice({
    name: 'counter',
    inintialState: 0,
    reducers: {
        increment: state => state + 1,
        decrement: state => state - 1
    }
}); 

const store = configureStore({
    reducer: {
        counter: counterSlice.reducer
    }
});

const Counter = () => {
    const count = useSelector(state => state.counter);
    const dispach = useDispatch();
    return <button onClick={() => dispatch(counterSlice.actions.increment())}{count}</button> 
}

3.中间件应用

const fetchUser = () => async (dispatch) => {
    dispatch({ type: 'USER_REQUEST' });
    try {
        const res = await api.getUser();
        dispatch({ type: 'USER_SUCCESS', payload: res.data });
    } catch (err) {
        dispatch({ type: 'USER_FAILURE', error: err.message });
    }
}

const logger = store => next => action => {
    console.log('dispatching:', action);
    let result = next(action);
    console.log('next state:', store.getState());
    return result;
}

5.状态管理选型

1.评估应用规模

  • 小型应用:Context + useState/useReducer
  • 中大型应用:Redux/Mobx

2.团队熟悉度

  • 已有Redux经验:Redux Toolit
  • 偏好响应式:Mobx

3.性能需求

  • 高频更新:Recoil原子状态
  • 复杂异步:Redux + Saga/Thunk

4.开发效率

  • 快速迭代:Zustand/Jotai
  • 长期维护:Redux(强约束性)

6.高频面试题

1.redux如何避免不必要的重新渲染?

  • 使用reselect创建记忆化Selector,避免重复计算
  • 结合React.memo对组件进行浅比较
const selectUser = state => state.user;
const selectUserName = createSelector(
  [selectUser],
  (user) => user.name
);

2.Mobx的响应式原理

  • 通过ES6 ProxyObject.defineProperty追踪属性访问,自动建立观察者-被观察者关系,状态变更时触发依赖组件更新

3.如何解决Context API的性能问题?

  • 拆分多个Context隔离变化
  • 使用useMemo缓存Provider的value
const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState('light');
    const value = useMemo(() => ({ theme, setTheme }), [theme]);
    return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}

4.状态管理的不可变性为何重要?

  • 确保状态变更可预测,便于调试追踪
  • 支持React的浅比较优化策略(如shouldComponentUpdate

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2026-08-27
定时器按顺序播放多个音频,切后台音频会乱
本身不会补触发,但 它会被系统级冻结 ——iOS Safari 后台完全停止计时,切回前台后 只补执行一次 (不是不补,而是"缺失的中间状态补不上")。间隔短的不会出大问题, 间隔长的会出现"音频流被压缩" ——切回来后本该播 5 分钟的间隙,实际只过了 3 分钟,结果整段对不上。 核心原则 :定时器只能用来"提醒一次", 真正决定"该不该播"的是绝对时间戳 。 setTimeout 在后台会发生什么(按平台) | 平台 | 后台行为...
面试
八股文2026-08-27
线上项目白屏的原因
白屏的本质是 渲染管线某一环断了 ——可能是资源、JS、接口、路由、样式、兼容性任一环节出问题。排查按"控制台 → 网络 → DOM → 环境"四步定位。 本质(前端类比) 把网页想成一栋楼: 白屏 ≠ 一定是同一种原因——这是面试想听的层次。 六大类原因(按出现频率) | 类别 | 典型表现 | 真实案例 | | : | : | : | | ① 资源加载失败 | DOM 是空的 | 入口 JS 404、CDN 挂了、CSS 阻塞 |...
面试
八股文2026-08-27
背景图就是 1MB 大图,怎么优化
1MB 大背景图优化分 三步 :压缩体积(10 30x)、按需加载(按设备/视口/网速)、渲染期优化(GPU 合成)。背景图跟 不同——它是 CSS,不走浏览器的原生懒加载机制,得手动处理。 背景图 vs 的关键区别(先讲清楚这个) 这是面试官想听的"针对性认知"——背景图不是普通图片,不能套通用方案。 三步优化(按优先级) 第 1 步:压缩体积(最重要,立竿见影) 1MB 的来源一般是这几种 ,对应解决方案: | 原始问题 | 体积来...
面试
八股文2026-08-27
项目里很多图片和视频,怎么优化
图片视频优化分 四层 :网络层(CDN/格式)、加载层(懒加载/预加载)、渲染层(解码/缓存)、业务层(按需/降级)。面试要把这四层都讲到位才算有体系。 四层优化模型 第 1 层:网络层(省钱、省时间) 核心目标:让资源体积小、让用户拿到资源快 | 手段 | 作用 | 关键点 | | : | : | : | | 图片格式 | WebP/AVIF 比 JPEG 小 25 50% | 兼容 fallback | | 视频格式 | H.265...
面试
八股文2026-08-20
Embedding 向量模型:从语义表示到相似度计算
前言 大模型「读懂」文字靠的是 token,但 token 之间只有离散的编号关系,模型并不知道「苹果」和「李子」在语义上很近。要让程序能「理解」两段文字的相似程度,必须先把文本映射成一个 高维向量 ,再用几何方法比较。这一步就是 Embedding。 本篇基于我本地 今天的真实代码,从语义表示讲到余弦相似度,并复盘几个踩过的真实坑。 一、为什么需要 Embedding 传统关键词检索是「字面匹配」: Embedding 做的是「语义匹...
面试
八股文2026-08-19
前端面试100题
前端面试 100 题 适用方向:中高级前端 / React / Vue / Next.js / Nuxt / TypeScript / 工程化 / 实时通信 / Electron / Node.js / AI 应用前端 使用方式:优先掌握“标准回答”,再练“面试官追问”,最后把“结合你的简历怎么答”组织成自己的项目故事。 说明 “结合你的简历怎么答”只使用你简历中已经出现的项目与技术事实。 “标准回答 / 追问”属于通用前端知识总结,用...
面试

评论

请登录后发表评论

去登录
加载评论中...

目录