react中高级面试题100道有解析

以下是 100 道 React 中高级面试题及解析,涵盖核心原理、Hooks、状态管理、性能优化等多个维度,适合中高级前端开发者备考参考。

一、React 核心原理(1-10 题)

1. 什么是虚拟 DOM?它为什么能提高性能?

解析

  • 虚拟 DOM(Virtual DOM)是 React 内部维护的、对真实 DOM 的轻量内存映射(JavaScript 对象),描述了 DOM 的结构和属性。
  • 性能优化逻辑:
    • 真实 DOM 操作昂贵(重绘 / 回流),虚拟 DOM 通过批量对比差异(Diff 算法),只更新变化的部分,减少真实 DOM 操作次数。
    • 虚拟 DOM 是 JS 对象,操作成本远低于真实 DOM,且支持跨平台(如 React Native)。
2. React 的 Diff 算法有哪些优化策略?

解析
React Diff 算法基于三个假设实现高效对比:

  1. 同层节点只对比同层:不跨层级对比(如父节点变化,直接销毁子树重建)。
  2. key 的作用:列表节点通过key标识唯一性,避免不必要的节点重排(无 key 时可能导致组件状态错乱)。
  3. 类型不同则销毁重建:若节点类型(如div vs span)不同,直接销毁旧节点并创建新节点,不深入对比子节点。
3. 什么是 Fiber 架构?它解决了什么问题?

解析

  • Fiber 是 React 16 引入的新协调引擎,本质是可中断、可恢复的工作单元(将渲染任务拆分为小片段)。
  • 解决的问题:
    传统 Stack reconciler(栈协调)是同步任务,若渲染任务复杂(如长列表),会阻塞主线程(导致 UI 卡顿、事件响应延迟)。
    Fiber 通过时间切片(Time Slicing) 和优先级调度(如用户输入 > 动画 > 渲染),允许任务中断、暂停、恢复或放弃,保证主线程不被阻塞。
4. React 的生命周期分为几个阶段?每个阶段有哪些常用生命周期方法?

解析
分为 3 个阶段:

  1. 挂载阶段(Mounting):组件初始化并插入 DOM

    • constructor:初始化状态、绑定方法
    • static getDerivedStateFromProps:从 props 派生 state(替代componentWillReceiveProps
    • render:生成虚拟 DOM
    • componentDidMount:DOM 挂载后执行(如请求数据、事件监听)
  2. 更新阶段(Updating):props/state 变化时

    • static getDerivedStateFromProps
    • shouldComponentUpdate:控制是否重渲染(默认 true,可优化性能)
    • render
    • getSnapshotBeforeUpdate:在 DOM 更新前获取快照(如滚动位置)
    • componentDidUpdate:DOM 更新后执行(如根据 props 变化请求数据)
  3. 卸载阶段(Unmounting)

    • componentWillUnmount:组件卸载前清理(如移除事件监听、取消请求)
5. React 的合成事件(SyntheticEvent)与原生事件有什么区别?

解析

维度合成事件原生事件
事件委托委托到 document(统一管理)需手动绑定到具体 DOM 节点
事件对象跨浏览器兼容的 SyntheticEvent 实例浏览器原生 Event 对象
阻止冒泡e.stopPropagation()除了e.stopPropagation(),还需e.preventDefault()(部分场景)
执行顺序先于原生事件(因委托机制)后于合成事件

注意:合成事件中e.nativeEvent可获取原生事件对象,但不建议混合使用两者(可能导致逻辑混乱)。

6. 什么是受控组件与非受控组件?如何选择?

解析

  • 受控组件:表单元素(如 input、select)的状态由 React 的 state 控制,通过onChange同步更新(如value={state} onChange={(e) => setState(e.target.value)})。
    优点:状态统一管理,可实时验证;缺点:代码略繁琐。

  • 非受控组件:表单状态由 DOM 自身维护,通过ref获取值(如ref={(el) => this.input = el},取值时用this.input.value)。
    优点:代码简洁;缺点:状态分散,难实时验证。

选择原则

  • 需实时验证、联动控制(如表单提交前校验)→ 受控组件;
  • 简单场景(如文件上传)、兼容第三方库 → 非受控组件。
7. React 中setState是同步还是异步的?为什么?

解析

  • 分场景

    • 在 React 合成事件(如onClick)、生命周期方法中:异步更新(批量合并,提升性能)。
    • 在原生事件(如addEventListener)、定时器(setTimeout)、Promise 回调中:同步更新(脱离 React 控制流)。
  • 原理:React 通过isBatchingUpdates标记是否批量更新。合成事件 / 生命周期中,该标记为truesetState会先入队列,结束后批量执行;其他场景标记为false,立即执行。

  • 如何获取更新后的值:使用setState的回调函数(setState(updater, callback))或useEffect(Hooks 中)。

8. 什么是 Context?它的使用场景和局限性是什么?

解析

  • Context 是 React 提供的跨组件数据传递方案(避免 “props drilling”:多层级传递 props)。
  • 使用场景:全局状态(如主题、用户信息)、跨少数层级的组件通信。
  • 使用方式

    jsx

    const ThemeContext = React.createContext('light'); // 创建Context
    // 父组件提供值
    <ThemeContext.Provider value="dark">
      <Child />
    </ThemeContext.Provider>
    // 子组件消费值
    const Child = () => {
      const theme = useContext(ThemeContext); // Hooks方式
      return <div>{theme}</div>;
    };
    
  • 局限性
    • 频繁更新会导致所有消费组件重渲染(性能问题),不适合频繁变化的状态;
    • 不能替代状态管理库(如 Redux),复杂状态建议用专门的库。
9. React 中key的作用是什么?为什么不能用索引作为key

解析

  • key是 React 识别列表节点唯一性的标识,用于优化 Diff 算法:若key不变,React 认为节点未变,可复用;若key变化,节点会被销毁重建。

  • 用索引作为key的问题:
    当列表增删、排序时,索引会变化(如删除第一项,后续项索引都减 1),导致 React 误判节点变化,引发:

    • 不必要的节点销毁重建(性能浪费);
    • 组件状态丢失(如输入框内容错乱)。
  • 推荐:用节点唯一 ID(如后端返回的id)作为key

10. React 与 Vue 的核心区别是什么?

解析

维度ReactVue
核心思想函数式编程(组件即函数)响应式编程(数据驱动)
模板JSX(JavaScript 中写 HTML)独立模板(HTML 中写指令)
状态管理需第三方库(Redux 等)内置 Vuex/Pinia
虚拟 DOM全量对比(Fiber 优化)基于依赖追踪的精确更新
学习曲线较陡(需理解 JSX、Hooks 等)较平缓(HTML/CSS/JS 友好)

二、Hooks 深入(11-25 题)

11. 什么是 Hooks?它解决了什么问题?

解析

  • Hooks 是 React 16.8 引入的特性,允许在函数组件中使用状态和生命周期功能(无需编写类组件)。
  • 解决的问题:
    • 类组件的生命周期逻辑分散(如componentDidMountcomponentDidUpdate可能写重复逻辑);
    • 高阶组件(HOC)和 Render Props 导致的 “嵌套地狱”;
    • 函数组件无法使用状态的限制。
12. useState的工作原理是什么?如何实现复杂状态管理?

解析

  • 原理:useState(initialState)返回[state, setState],其中state是当前状态,setState是更新函数。React 通过闭包保存状态,每次调用setState会触发组件重渲染,并更新闭包中的状态。

  • 复杂状态管理:

    • 多个useState:拆分独立状态(推荐,逻辑清晰);
    • useReducer:处理多状态联动(如表单多字段验证);
    • 注意:setState是合并更新(对象状态需手动展开,如setState(prev => ({...prev, name: 'new'})))。
13. useEffect的依赖数组有什么作用?空依赖数组代表什么?

解析

  • useEffect(effect, deps)中,deps是依赖数组,用于控制effect的执行时机:

    • deps省略:每次组件重渲染都会执行effect和清理函数;
    • deps为空[]:仅在组件挂载后执行一次effect,卸载时执行清理函数(类似componentDidMountcomponentWillUnmount);
    • deps有值:仅当数组中任意值变化时,才执行effect(先执行上一次的清理函数,再执行新的effect)。
  • 常见错误:依赖数组遗漏依赖(如effect中使用了state/props但未加入deps),会导致effect捕获旧值(闭包问题)。

14. 什么是useRef?它与createRef有什么区别?

解析

  • useRef(initialValue)返回一个可变的ref对象({current: initialValue}),特点:

    • ref.current的变化不会触发组件重渲染;
    • 可保存 DOM 节点(如input)或跨渲染周期的变量(如定时器 ID)。
  • createRef的区别:

    • useRef是 Hooks,仅用于函数组件,会在每次渲染时返回同一个对象(持久化);
    • createRef用于类组件,每次渲染会创建新对象
15. useCallbackuseMemo的作用是什么?有什么区别?

解析

  • 共同点:均用于性能优化,避免不必要的计算或重渲染。

  • 区别:

    • useCallback(fn, deps):缓存函数(返回记忆化的函数),避免函数引用变化导致子组件(接收该函数为 props)重渲染。
    • useMemo(valueFactory, deps):缓存计算结果(返回记忆化的值),避免每次渲染重复执行昂贵计算(如大数据排序)。
  • 使用场景:

    • 传递给子组件的回调函数 → useCallback
    • 复杂计算的结果 → useMemo
16. 什么是 Hooks 的闭包陷阱?如何避免?

解析

  • 闭包陷阱:Hooks 依赖闭包保存状态,若effect/useCallback等的依赖数组未正确设置,会导致它们捕获旧的状态 /props,而非最新值。
    例:

    jsx

    const [count, setCount] = useState(0);
    useEffect(() => {
      setInterval(() => {
        console.log(count); // 始终打印0,因捕获了初始count
      }, 1000);
    }, []); // 依赖为空,effect只执行一次
    
  • 避免方式:

    1. 将依赖加入依赖数组(如[count]);
    2. 若依赖频繁变化,用useRef保存最新值(绕过闭包):

      jsx

      const countRef = useRef(count);
      useEffect(() => { countRef.current = count; }, [count]); // 实时更新ref
      useEffect(() => {
        setInterval(() => { console.log(countRef.current); }, 1000);
      }, []);
      
17. useReducer的使用场景是什么?与useState相比有什么优势?

解析

  • useReducer(reducer, initialState)接收一个 reducer 函数和初始状态,返回[state, dispatch],通过dispatch(action)触发状态更新(类似 Redux)。

  • 适用场景:

    • 状态逻辑复杂(多状态联动,如表单验证、购物车加减);
    • 状态更新依赖前一个状态(reducer中可安全获取prevState);
    • 需要预测状态变化(reducer 是纯函数,便于测试)。
  • useState对比:

    • 优势:逻辑集中(reducer 函数统一管理更新),避免多个setState分散;
    • 劣势:简单状态用useState更简洁。
18. useContextuseReducer结合使用有什么好处?

解析

  • 结合后可实现 “轻量级状态管理”,无需引入 Redux:

    1. useReducer管理复杂状态和更新逻辑;
    2. useContextstatedispatch传递给子组件,避免 props 层级传递。
  • 示例:

    jsx

    // 1. 创建Context
    const StoreContext = createContext();
    // 2. 定义reducer
    const reducer = (state, action) => {/* ... */};
    // 3. 父组件提供状态
    const Parent = () => {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <StoreContext.Provider value={{ state, dispatch }}>
          <Child />
        </StoreContext.Provider>
      );
    };
    // 4. 子组件消费
    const Child = () => {
      const { state, dispatch } = useContext(StoreContext);
      return <button onClick={() => dispatch({ type: 'ADD' })}>{state.count}</button>;
    };
    
19. 自定义 Hooks 的定义和使用规则是什么?

解析

  • 自定义 Hooks 是封装复用逻辑的函数,命名以use开头(如useWindowSize),内部可调用其他 Hooks。

  • 规则:

    1. 必须以use开头(React 通过命名检测 Hooks 调用位置,确保只在函数组件 / 自定义 Hooks 中调用);
    2. 内部可调用其他 Hooks(如useStateuseEffect),但需遵循 Hooks 调用规则(不能在条件、循环中调用)。
  • 示例(监听窗口大小):

    jsx

    function useWindowSize() {
      const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
      useEffect(() => {
        const handleResize = () => {
          setSize({ width: window.innerWidth, height: window.innerHeight });
        };
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
      }, []);
      return size;
    }
    
20. useLayoutEffectuseEffect的区别是什么?

解析

  • 执行时机:

    • useEffect:在 DOM 渲染完成后异步执行(不阻塞浏览器绘制);
    • useLayoutEffect:在 DOM 更新后、浏览器绘制前同步执行(阻塞绘制)。
  • 使用场景:

    • useEffect:大多数场景(如数据请求、事件监听);
    • useLayoutEffect:需在绘制前修改 DOM(如避免布局抖动,如计算元素位置后立即调整)。
  • 注意:useLayoutEffect可能导致性能问题(阻塞 UI),非必要不使用。

21. 如何用 Hooks 实现类组件的getDerivedStateFromProps

解析
getDerivedStateFromProps用于从 props 派生 state(类组件中),Hooks 中可通过以下方式实现:

  1. 直接在函数组件中根据 props 计算状态(适用于简单场景):

    jsx

    const [state, setState] = useState(initialState);
    useEffect(() => {
      setState(prev => computeStateFromProps(props, prev)); // 当props变化时更新state
    }, [props]); // 依赖props
    
  2. 注意:避免 “派生状态” 过度使用(可能导致状态来源混乱),优先考虑:

    • 直接使用 props(无需保存为 state);
    • useMemo缓存计算结果(而非存入 state)。
22. 如何用 Hooks 实现防抖 / 节流?

解析
结合useRef(保存定时器)和useCallback(缓存函数)实现:

  • 防抖(示例):

    jsx

    function useDebounce(fn, delay) {
      const timerRef = useRef(null);
      return useCallback((...args) => {
        if (timerRef.current) clearTimeout(timerRef.current);
        timerRef.current = setTimeout(() => {
          fn.apply(this, args);
        }, delay);
      }, [fn, delay]);
    }
    // 使用:
    const debouncedSearch = useDebounce((value) => { console.log('搜索:', value); }, 500);
    
  • 节流(示例):

    jsx

    function useThrottle(fn, interval) {
      const lastTimeRef = useRef(0);
      return useCallback((...args) => {
        const now = Date.now();
        if (now - lastTimeRef.current >= interval) {
          fn.apply(this, args);
          lastTimeRef.current = now;
        }
      }, [fn, interval]);
    }
    
23. useImperativeHandle的作用是什么?

解析
useImperativeHandle(ref, createHandle, deps)用于自定义暴露给父组件的 ref 方法,避免父组件直接操作子组件 DOM(封装性更好)。

  • 示例:

    jsx

    // 子组件
    const Child = forwardRef((props, ref) => {
      const inputRef = useRef(null);
      // 自定义ref暴露的方法
      useImperativeHandle(ref, () => ({
        focus: () => { inputRef.current.focus(); },
        clear: () => { inputRef.current.value = ''; }
      }), []);
      return <input ref={inputRef} />;
    });
    // 父组件
    const Parent = () => {
      const childRef = useRef(null);
      return (
        <div>
          <button onClick={() => childRef.current.focus()}>聚焦</button>
          <Child ref={childRef} />
        </div>
      );
    };
    
24. useDebugValue的作用是什么?

解析
useDebugValue(value, formatter)用于在 React DevTools 中自定义 Hooks 的显示名称和值,方便调试。

  • 示例:

    jsx

    function useUser(id) {
      const [user, setUser] = useState(null);
      useEffect(() => {
        fetch(`/api/user/${id}`).then(res => setUser(res.data));
      }, [id]);
      // 在DevTools中显示为“User: 用户名”(而非原始值)
      useDebugValue(user, user => user ? `User: ${user.name}` : 'Loading...');
      return user;
    }
    
25. 为什么 Hooks 不能在条件语句、循环或嵌套函数中调用?

解析
React 通过Hooks 调用顺序来关联组件状态(每次渲染时,Hooks 的调用顺序必须一致)。若在条件 / 循环中调用,会导致:

  • 调用顺序混乱,React 无法正确匹配 Hooks 与状态,引发状态错乱。

例(错误):

jsx

function Component() {
  if (condition) {
    const [a, setA] = useState(0); // 条件中调用,可能导致后续Hooks顺序错误
  }
  const [b, setB] = useState(1); // 若condition为false,此处会被视为第一个Hooks,与上次渲染顺序不一致
}

三、状态管理(26-40 题)

26. Redux 的核心概念是什么?工作流程是怎样的?

解析

  • 核心概念:

    • Store:保存整个应用的状态树(唯一);
    • Action:描述状态变化的普通对象(必须有type字段,如{ type: 'ADD', payload: 1 });
    • Reducer:纯函数((state, action) => newState),根据 action 更新 state;
    • Dispatch:发送 action 的方法(store.dispatch(action))。
  • 工作流程:

    1. 组件通过dispatch发送 action;
    2. Redux 调用 reducer,根据 action 计算新 state;
    3. Store 更新 state,触发组件重新渲染(通过subscribe监听)。
27. Redux 为什么要求 reducer 是纯函数?

解析
纯函数的特性:

  • 相同输入必返回相同输出;
  • 无副作用(不修改参数、不调用 API、不操作 DOM 等)。

Redux 依赖纯函数的特性实现:

  • 可预测性:状态变化完全由 action 和当前 state 决定,便于调试和测试;
  • 可回溯:纯函数无副作用,支持时间旅行(如 Redux DevTools 的状态回退);
  • 一致性:避免意外修改 state(reducer 必须返回新 state,而非修改原 state)。
28. Redux 中间件的作用是什么?常用的中间件有哪些?

解析

  • 作用:扩展 Redux 的功能,处理异步 action(如 API 请求)、日志记录、错误处理等(Redux 默认只支持同步 action)。

  • 常用中间件:

    • redux-thunk:允许 action 是函数(而非对象),在函数中异步 dispatch action(如(dispatch) => { fetch().then(res => dispatch(action)) });
    • redux-saga:用 generator 函数处理复杂异步逻辑(如取消请求、重试、竞态处理);
    • redux-logger:打印 action 和 state 变化日志,便于调试。
29. redux-thunkredux-saga的区别是什么?如何选择?

解析

维度redux-thunkredux-saga
语法基于函数(简单直观)基于 generator(略复杂)
异步控制弱(依赖 Promise 链式调用)强(支持取消、重试、节流)
错误处理try/catch 或 Promise.catchtry/catch(generator 特性)
测试难度较简单较复杂(需处理 generator)
适用场景简单异步(如单次 API 请求)复杂异步(如长轮询、并发)

选择:简单场景用thunk,复杂异步逻辑用saga

30. Redux 的combineReducers有什么作用?

解析

  • 作用:将多个 reducer 合并为一个根 reducer(因 Redux store 只能有一个根 reducer),实现状态树的模块化拆分。

  • 示例:

    jsx

    // 拆分reducer
    const userReducer = (state = {}, action) => {/* ... */};
    const cartReducer = (state = [], action) => {/* ... */};
    // 合并
    const rootReducer = combineReducers({
      user: userReducer,
      cart: cartReducer
    });
    // store的state结构为 { user: {...}, cart: [...] }
    
31. React-Redux 的connect函数作用是什么?mapStateToPropsmapDispatchToProps的作用?

解析

  • connect:将 Redux store 与 React 组件连接,使组件能获取 state 和 dispatch action。

  • mapStateToProps(state, ownProps)

    • 接收 store 的 state 和组件自身 props,返回一个对象(作为 props 传入组件),使组件能访问 Redux 状态。
    • 例:(state) => ({ count: state.count }) → 组件可通过this.props.count访问。
  • mapDispatchToProps(dispatch, ownProps)

    • 接收 dispatch 方法,返回一个对象(包含函数,作为 props 传入组件),使组件能 dispatch action。
    • 例:(dispatch) => ({ add: () => dispatch({ type: 'ADD' }) }) → 组件可通过this.props.add()触发 action。
32. Redux Toolkit(RTK)解决了 Redux 的哪些问题?

解析
Redux 原生使用繁琐(需手动写 action type、reducer、immer 处理不可变数据等),RTK 是官方推荐的工具集,解决:

  1. 样板代码过多createSlice自动生成 action type 和 creator;
  2. 不可变数据处理复杂:内置immer,允许 “直接修改” state(实际生成新 state);
  3. 异步逻辑繁琐createAsyncThunk简化异步 action 创建;
  4. store 配置复杂configureStore默认集成redux-thunk、开发者工具等。

示例(createSlice):

jsx

import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; } // 直接修改(immer处理)
  }
});
export const { increment } = counterSlice.actions; // 自动生成action creator
export default counterSlice.reducer;
33. 什么是 Redux 的不可变性(Immutability)?为什么重要?

解析

  • 不可变性:state 一旦创建,不能直接修改,只能返回新的 state 对象(如return {...state, count: state.count + 1})。

  • 重要性:

    • 便于追踪状态变化(对比新旧 state 是否引用相同,即可判断是否变化);
    • 支持时间旅行(Redux DevTools 需保存每一次 state 快照);
    • 避免意外副作用(防止多个地方修改同一 state 导致的混乱)。
34. MobX 的核心概念是什么?与 Redux 相比有什么区别?

解析

  • MobX 核心概念:

    • Observable:标记可观察状态(如observable({ count: 0 }));
    • Action:修改 observable 状态的函数(需用action标记,确保状态变更可追踪);
    • Reaction:响应状态变化的副作用(如autorunreaction)。
  • 与 Redux 的区别:

    维度ReduxMobX
    范式函数式(单向数据流)面向对象(响应式)
    状态修改必须通过 action 和 reducer直接修改(action 标记)
    样板代码多(需定义 action、reducer)少(简洁直观)
    可预测性高(严格规范)较低(灵活但易混乱)
35. 什么是 Recoil?它解决了什么问题?

解析
Recoil 是 Facebook 推出的状态管理库,专为 React 设计,解决:

  • Context 的性能问题(频繁更新导致所有消费组件重渲染);
  • Redux 的样板代码冗余。

核心概念:

  • Atom:可共享的状态单元(类似 Redux 的 state 片段,如const countAtom = atom({ key: 'count', default: 0 }));
  • Selector:基于 atom 或其他 selector 的派生状态(纯函数计算,如const doubleCount = selector({ key: 'double', get: ({get}) => get(countAtom) * 2 }));
  • 组件通过useRecoilState/useRecoilValue消费状态,仅当依赖的 atom/selector 变化时才重渲染。
36. Zustand 的特点是什么?适合什么场景?

解析
Zustand 是轻量级状态管理库,特点:

  • 极简 API(无需 Provider 包裹);
  • 支持中间件(如持久化、日志);
  • 与 React Hooks 无缝集成。

使用示例:

jsx

import create from 'zustand';
const useStore = create((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
}));
// 组件中使用
const Component =

编辑

分享

生成一份包含100道React中高级面试题及解析的文档

React中如何优化组件的性能?

分享一些关于React状态管理的实际项目经验

深度思考: 自动

技能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值