React面试题

本文最后更新于 2025年11月13日 凌晨

Component、Hook、Utils

类型 封装 渲染 UI 依赖生命周期 使用场景
react 组件 UI 结构逻辑 页面、模块、UI 片段
自定义 hook 可复用的逻辑状态 状态逻辑、数据请求、订阅等
utils 函数 纯逻辑通用计算 格式化、计算、API 调用等

react 组件

  • 特征:能返回 UI(JSX)
  • 用途:构建界面(View 层)
  • 特点:
    • 可以有 state
    • 可以有 props
    • 可以渲染 HTML 结构
    • 可以使用 Hook(包括自定义 Hook)
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    function UserCard({ user }) {
    return (
    <div className="card">
    <img src={user.avatar} />
    <p>{user.name}</p>
    </div>
    )
    }

✅ 用来展示界面(UI)
⚠️ 不推荐在组件中堆太多逻辑,应交给 hook 或 utils

自定义 hook

  • 特征:以 use 开头、封装状态逻辑,可在多个组件中共享
  • 用途:封装“与 React 生命周期有关”的逻辑,比如:
    • 状态管理(useState、useReducer)
    • 副作用(useEffect)
    • 数据请求(fetch、WebSocket)
    • DOM 监听(scroll、resize)
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { useState, useEffect } from 'react'

    function useUserInfo() {
    const [user, setUser] = useState(null)

    useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser)
    }, [])

    return user
    }

    export default useUserInfo

✅ 可以被多个组件复用逻辑
❌ 不渲染 UI,自身返回的是“数据和行为”

utils 函数函数

  • 特征:纯逻辑、无状态、无 React 依赖
  • 用途:做纯计算、格式化、工具类操作
  • 场景:
    • 时间格式化(formatDate()
    • 金额处理(formatPrice()
    • 深拷贝、节流防抖
    • 网络请求封装(如 request()
  • 示例:
    1
    2
    3
    export function formatPrice(price: number) {
    return '¥' + price.toFixed(2)
    }

✅ 任何 JS 环境都能用
✅ 不依赖 React
✅ 可在 Node、Vue 等项目中重用

三者关系

1
2
3
4
5
6
[ utils ] ← 最底层(纯函数层)

[ hooks ] ← 逻辑复用层(依赖 React)

[ components ] ← UI 展示层

  • utils:通用工具逻辑
  • hook:封装 React 状态逻辑
  • component:渲染 UI

React 和 Vue 的区别

React 和 Vue 都是流行的前端框架,但有一些核心区别:

  • React 更偏向函数式编程和单向数据流,本质是 UI 库,需要依赖社区生态来解决路由、状态管理等问题;而 Vue 是渐进式框架,开箱即用,支持双向绑定,学习曲线更平滑。
  • 在视图层上,React 使用 JSX,逻辑和视图融合度更高,更灵活;Vue 使用模板和指令,写法更直观。
  • 响应式上,React 借助 Fiber 架构和 Virtual DOM 来驱动更新,而 Vue 2 用 defineProperty,Vue 3 用 Proxy,响应式能力更强。

总体来看,React 更适合大型复杂应用和跨端场景,Vue 更适合快速开发和中小型项目。

为什么要用 Hook

  • 解决逻辑复用难的问题。类组件里想复用逻辑,只能用高阶组件 (HOC) 或 Render Props,会导致组件嵌套地狱。Hooks 允许把状态逻辑提取到自定义 Hook 中,实现逻辑共享
  • 解决类组件复杂度高的问题。一个功能的逻辑可能分散在多个生命周期函数:订阅在 componentDidMount、更新在 componentDidUpdate、清理在 componentWillUnmount。Hooks 用 useEffect 就能把逻辑集中到一起,更符合“关注点分离”的设计
  • 解决 this 问题。类组件里 this 经常出错,需要手动绑定。函数组件没有 this 彻底解决此困扰
  • 更好支持函数式编程和 Fiber 架构。函数组件 + Hook 更契合 React 声明式和可中断渲染。在执行过程能暂停、恢复甚至丢弃函数组件的执行,而类组件里状态绑定在实例上不好做

为什么只能在函数内部最外层调用 Hook

因为 React 使用单向链表来存储每个组件调用的 Hook 状态,每个 Hook 节点都存储着它的状态(如 state、effect 函数、依赖项等)。React 通过调用顺序来判断哪个 Hook 对应哪个状态。

函数组件的每次渲染都是完全独立的。也就是说,每个函数中的 state 变量只是一个简单的常量,每次渲染时从钩子中获取到的常量,没有附着数据绑定(除了 useRef.current 返回是引用)。

所以如果把 Hook 放在条件语句/循环/子函数里,就会破坏调用顺序,React 无法正确找到对应的 Hook 状态。

为什么只能在 React 函数组件或自定义 Hook 中用 Hook

因为 Hooks 必须被关联到一个具体的 React 组件实例上。React 内部有一个当前正在渲染组件的指向(可以理解为一个全局的“光标”)。当调用 Hook 时 React 会检查这个“光标”,知道这个状态是属于哪个组件的,然后把它挂载到对应的 Fiber 节点(React 内部表示组件的结构)上。

React 性能优化

  • React.memo、shouldCompountUpdata、useMemo、useCallBack
  • index、key
  • render、useTransition、useDeferredValue
  • React.lanzy、Suspance
  • useContext
  • useEffect、return
  • useRuducer

来源:React 性能优化十大总结React 性能优化深度指南:从基础到高级技巧

React18 新特性

  • render、useTransition、useDeferredValue
  • setSate 批处理、flushSync

React diff 算法过程

在 React 中,diff 算法需要与虚拟 DOM 配合才能发挥出真正的效果。React 会使用 diff 算法算出虚拟 DOM 中真正发生变化的部分,并且只会针对该部分进行 DOM 操作,从而避免了对页面进行大面积的更新渲染,减小性能的开销。

React 定义了三大策略,在对比时根据策略只需遍历一次树就可以完成对比,复杂度降到了 O(n)

  • tree diff:在两个树对比时,只会比较同一层级的节点,忽略跨层级的操作
  • component diff:在对比两个组件时,首先会判断它们两个的类型是否相同,如果不同直接替换整个组件下的所有子节点
  • element diff:同一层级的节点使用唯一性的 key 来区分是否需要创建、删除、移动

为何 React JSX 循环需要使用 key

React 通过 key 唯一标识列表中的每个元素。当列表发生变化时,React 会通过 key 快速判断,哪些元素是新增、哪些是移除、哪些是移动,从而提高性能。如果没有 key,React 默认使用数组索引作为标识,会认为所有 key 变化的元素都是新节点,从而进行不必要的重建,而不是高效的移动,导致性能下降和潜在的状态 bug。

精读《useEffect 完全指南》

深入理解受控组件、非受控组件

setState 是同步还是异步

React-Router 工作原理

一文带你梳理 React 面试题(2023 年版本)

2025 最新 React 面试题


React面试题
https://xuekeven.github.io/2025/08/24/React面试题/
作者
Keven
发布于
2025年8月24日
更新于
2025年11月13日
许可协议