【前端高频面试题】- React篇 - 指南

news/2025/9/22 10:27:47/文章来源:https://www.cnblogs.com/tlnshuju/p/19104673

【前端高频面试题】- React篇 - 指南

2025-09-22 10:23  tlnshuju  阅读(0)  评论(0)    收藏  举报

【前端高频面试题】- React篇

1. 请说说 React 的核心概念有哪些?

React 核心概念围绕“组件化”和“高效渲染”设计,核心包括以下几点:

2. 什么是 JSX?为什么 React 要使用 JSX?

JSX 是 React 定义 UI 的核心语法,面试需明确其本质和优势:

  • 定义:JSX(JavaScript XML)是 JavaScript 的语法扩展,允许在 JavaScript 代码中嵌入类似 HTML 的标签,用于描述 React 组件的 UI 结构。
    • 注意:JSX 不是 HTML,也不是模板语言,最终会被 Babel 编译为 React.createElement(type, props, children) 函数调用,生成虚拟 DOM 对象。
  • 使用 JSX 的原因
    1. 直观易读:类似 HTML 的语法,开发者无需切换“JS 逻辑”和“HTML 结构”思维,降低学习和开发成本。
    2. 融合 JS 逻辑:JSX 中可直接通过 {} 嵌入 JS 表达式(如变量、函数调用、条件判断),实现“UI 与逻辑的紧密结合”(如 {user.name} 渲染用户名称)。
    3. React 优化支持:JSX 编译后生成的虚拟 DOM 结构,是 React Diff 算法和高效渲染的基础,避免手动编写 React.createElement 冗余代码。

3. 虚拟 DOM 是什么?为什么 React 需要虚拟 DOM?

虚拟 DOM 是 React 性能优化的核心,需解释“是什么”和“为什么”:

  • 定义:虚拟 DOM(Virtual DOM)是用 JavaScript 对象描述真实 DOM 节点的轻量级结构,包含节点的标签名、属性、子节点等信息(如 { type: 'div', props: { className: 'box' }, children: [] })。
  • 为什么需要虚拟 DOM
    1. 减少真实 DOM 操作:真实 DOM 是浏览器渲染核心,操作成本极高(修改 DOM 会触发重排/重绘)。虚拟 DOM 作为“中间层”,先在 JS 中对比新旧虚拟 DOM 的差异,只将差异部分更新到真实 DOM,大幅减少操作次数。
    2. 跨平台支持:虚拟 DOM 脱离浏览器环境,React 可基于虚拟 DOM 适配不同平台(如 React Native 用虚拟 DOM 映射原生组件,而非浏览器 DOM)。
    3. 批量更新优化:React 可将多次状态更新合并为一次虚拟 DOM 对比,避免频繁触发真实 DOM 更新,进一步提升性能。

4. State 和 Props 的区别是什么?

State 和 Props 是组件数据的核心,面试需明确“来源、可变性、用途”三方面差异:

对比维度StateProps
数据来源组件内部定义,由组件自身管理父组件传递,组件外部输入
可变性可变(需通过 setState/useState 触发更新,不可直接修改)只读(子组件不能修改 Props,若需修改需通知父组件更新)
用途管理组件内部动态状态(如输入框值、弹窗显隐)实现组件间通信(父传子)、传递静态/动态数据
更新触发组件内部调用更新方法(如 setState)触发自身重渲染父组件更新 Props 时,子组件被动重渲染

5. React 类组件的生命周期有哪些?函数组件用什么替代生命周期?

类组件生命周期是基础,需分阶段梳理;函数组件需关联 Hooks 替代方案:

(1)类组件生命周期(3 个阶段)

  1. 挂载阶段(组件创建到 DOM 渲染)
    • constructor:初始化 State、绑定函数(如 this.handleClick = this.handleClick.bind(this))。
    • render:返回 JSX 结构,生成虚拟 DOM(不能调用 setState,否则会触发无限重渲染)。
    • componentDidMount:组件挂载到真实 DOM 后执行,常用于初始化操作(如发起异步请求、绑定事件监听)。
  2. 更新阶段(组件状态/Props 变化)
    • shouldComponentUpdate(nextProps, nextState):返回布尔值,决定组件是否需要重渲染(默认 true,可手动优化性能,如对比新旧 Props/State)。
    • render:重新生成虚拟 DOM,对比差异。
    • componentDidUpdate(prevProps, prevState):组件更新后执行,常用于根据更新后的 DOM 做操作(如根据新 Props 请求数据,需判断 prevProps !== this.props 避免重复请求)。
  3. 卸载阶段(组件从 DOM 移除)
    • componentWillUnmount:组件卸载前执行,用于清理资源(如取消异步请求、移除事件监听、清除定时器),避免内存泄漏。

(2)函数组件替代方案

函数组件无生命周期,通过 Hooks 实现相同逻辑:

  • useEffect:覆盖挂载、更新、卸载阶段,如:
    • 模拟 componentDidMountuseEffect(() => { /* 初始化操作 */ }, [])(依赖空数组,只执行一次)。
    • 模拟 componentDidUpdateuseEffect(() => { /* 更新后操作 */ }, [deps])(依赖指定变量,变量变化时执行)。
    • 模拟 componentWillUnmountuseEffect(() => { return () => { /* 清理操作 */ } }, [])(返回清理函数,组件卸载时执行)。

6. useState 的使用方法及注意事项是什么?

useState 是函数组件管理状态的基础,需讲清“用法”和“坑点”:

  • 使用方法

    1. 导入:import { useState } from 'react'
    2. 初始化:const [state, setState] = useState(初始值),其中:
      • state:当前状态值(如输入框内容、列表数据)。
      • setState:更新状态的函数(必须调用此函数更新,不能直接修改 state)。
    3. 示例:const [count, setCount] = useState(0); setCount(count + 1);(点击按钮时更新计数)。
  • 注意事项

    1. 初始值只在首次渲染生效:若初始值是复杂计算(如 useState(heavyCompute())),首次渲染后不会再执行,若需动态初始值,可传函数(useState(() => heavyCompute()))。
    2. setState 是异步的:在合成事件(如 onClick)、useEffect 回调中,setState 会批量更新,无法立即获取最新 state(如 setCount(count + 1); console.log(count) 仍输出旧值),需通过回调函数获取最新值(setCount(prev => prev + 1))。
    3. 更新对象/数组需浅拷贝:state 是引用类型时,不能直接修改原数据(如 const [user, setUser] = useState({ name: 'a' }); user.name = 'b' 无效),需浅拷贝后更新(setUser({ ...user, name: 'b' }))。

7. useEffect 的作用及依赖项如何设置?

useEffect 是函数组件处理“副作用”的核心,需明确“作用”和“依赖项规则”:

  • 作用
    副作用指“组件渲染外的操作”(如数据请求、DOM 操作、事件监听、定时器),useEffect 用于统一管理这些操作,并控制其执行时机(挂载、更新、卸载)。

  • 基本语法

    useEffect(() => {
    // 副作用逻辑(如发起请求、绑定监听)
    return () => {
    // 清理逻辑(如取消请求、移除监听),组件卸载或依赖变化前执行
    };
    }, [deps]); // 依赖项数组,控制 useEffect 何时执行
  • 依赖项设置规则

    1. 空数组 []:只执行一次(组件挂载时执行副作用,卸载时执行清理),模拟 componentDidMount + componentWillUnmount
    2. 指定依赖 [a, b]:副作用和清理逻辑会在组件挂载时执行,且当 ab 变化时重新执行(模拟 componentDidUpdate)。
    3. 不写依赖项:每次组件渲染(包括初始渲染和更新)都会执行副作用和清理逻辑,可能导致性能问题,不推荐
    4. 依赖项必须完整useEffect 内部使用的所有外部变量(如 stateprops、函数)都必须加入依赖数组,否则会触发“闭包陷阱”(获取到过时的变量值)。

8. React 中组件通信的方式有哪些?

组件通信是实战核心,需按“组件关系”分类说明,覆盖常用场景:

  1. 父组件 → 子组件(父传子)

    • 方式:通过 Props 传递数据(如 const Parent = () => <Child name="小明" age={18} />),子组件通过参数接收 const Child = (props) => <div>{props.name}</div>
    • 适用:直接父子关系,传递简单数据或回调函数。
  2. 子组件 → 父组件(子传父)

    • 方式:父组件传递回调函数给子组件,子组件调用该函数并传递数据(如 ParentonChange={handleChildData}ChildonClick={() => onChange(childData)})。
    • 适用:直接父子关系,子组件需通知父组件更新状态(如输入框子组件传递输入值)。
  3. 兄弟组件通信

    • 方式:共同父组件中转(兄弟 A 传数据给父,父再通过 Props 传给兄弟 B);或用 Context API(兄弟层级深时)。
    • 适用:同一父组件下的兄弟组件(如两个按钮组件联动)。
  4. 跨层级组件通信(祖孙/深层)

    • 方式 1:Context API(创建全局 Context,祖先组件提供数据,深层子组件消费数据,避免 Props 透传)。
    • 方式 2:状态管理库(如 Redux、Zustand,中大型应用中管理全局状态,如用户信息、购物车)。
    • 适用:组件层级深(如 3 层以上),且需共享数据(如主题色、登录状态)。
  5. 非嵌套组件通信

    • 方式:事件总线(如 mitt 库,订阅/发布模式,组件 A 发布事件,组件 B 订阅事件);或状态管理库。
    • 适用:无直接层级关系的组件(如两个页面组件)。

9. 列表渲染中 key 的作用是什么?为什么不能用 index 作为 key?

key 是列表渲染的性能关键,需解释“作用”和“index 隐患”:

10. React 事件处理和原生 DOM 事件有什么区别?

React 事件是“合成事件”,需对比核心差异:

对比维度React 合成事件(SyntheticEvent)原生 DOM 事件
绑定方式驼峰命名(如 onClick),写在组件标签上小写命名(如 onclick),或 addEventListener 绑定
事件对象合成事件对象(兼容所有浏览器,封装原生 Event)原生 Event 对象(浏览器差异大)
事件委托事件委托到 document(React 17 后委托到根组件),统一管理事件可委托到任意 DOM 元素(需手动调用 addEventListener
阻止默认行为直接调用 e.preventDefault()(无兼容问题)部分浏览器需先调用 e.returnValue = false(如 IE)
事件池(17 前)合成事件对象会被回收,事件回调后无法访问(需用 e.persist() 保留)原生事件对象不会被回收,可一直访问

11. React 中条件渲染的方式有哪些?

条件渲染是控制 UI 显示的基础,需列举常用方式及适用场景:

  1. if-else 语句

  2. 三元运算符

  3. 逻辑与 &&

  4. switch-case 语句

  5. 元素变量

12. useContext 的作用及使用步骤是什么?

useContext 是跨层级通信的基础方案,需讲清“作用”和“三步使用法”:

  • 作用:解决“Props 透传”问题(深层子组件需数据时,无需通过中间无关组件层层传递 Props),实现跨层级组件共享数据(如主题色、登录用户信息)。

  • 使用步骤(3 步)

    1. 创建 Context:用 React.createContext() 创建 Context 对象,可指定默认值(仅在无匹配 Provider 时生效)。
      import { createContext } from 'react';
      const ThemeContext = createContext('light'); // 默认主题:浅色
    2. 提供 Context 数据:用 Context.Provider 组件包裹子组件树,通过 value 属性传递数据(所有子组件可消费该数据)。
      const App = () => {
      const [theme, setTheme] = useState('dark'); // 当前主题:深色
      return ({/* 子组件 */}{/* 深层子组件 */}
      );
      };
    3. 消费 Context 数据:在深层子组件中用 useContext(Context) 获取数据,无需 Props 传递。
      import { useContext } from 'react';
      const Content = () => {
      const { theme, setTheme } = useContext(ThemeContext);
      return (
      );
      };
  • 注意事项

    • Context 更新会触发所有消费它的组件重渲染,可配合 React.memo 优化(避免无关组件重渲染)。
    • 默认值不是“ fallback ”,若 Provider 的 valueundefined,消费组件会使用默认值,而非 undefined

13. React 中的状态管理方案有哪些?各自适用场景是什么?

状态管理需按“应用规模”分类,覆盖实习常考方案:

  1. useState + useContext(轻量方案)

    • 原理:useState 管理状态,useContext 实现跨层级传递。
    • 适用场景:小型应用、简单全局状态(如主题、用户登录状态),无需引入额外库,成本低。
    • 缺点:状态复杂时(如多组件修改同一状态),逻辑分散,难以维护;Context 更新会触发大量组件重渲染(需手动优化)。
  2. Redux(中大型应用方案)

    • 原理:基于“单一数据源”“状态只读”“ reducer 纯函数更新”三大原则,通过 store 存储状态,action 描述修改意图,reducer 处理状态更新。
    • 配套库:react-redux(连接 React 和 Redux)、redux-thunk(处理异步 action)、redux-saga(复杂异步逻辑)。
    • 适用场景:中大型应用、状态复杂且多组件共享(如电商购物车、订单状态),需统一管理状态和副作用,便于调试(用 Redux DevTools 追踪状态变化)。
    • 缺点:配置繁琐,学习成本高,小型应用没必要使用。
  3. Zustand(轻量替代方案)

    • 原理:基于 React Context,但 API 更简洁,无需 Provider 包裹,直接创建 store,组件用 useStore 钩子获取/修改状态。
    • 特点:支持异步逻辑(无需额外库)、内置状态选择器(避免不必要重渲染)、体积小(约 1KB)。
    • 适用场景:中小型应用,想替代 Redux 的繁琐配置,同时需要全局状态管理(如后台管理系统的表格筛选状态)。
    • 优点:学习成本低,代码简洁,性能优于 useState + useContext
  4. Recoil/Jotai(细粒度状态管理)

    • 原理:将全局状态拆分为“原子(atom)”,组件只订阅需要的原子状态,原子更新时只触发订阅它的组件重渲染。
    • 适用场景:需要细粒度控制重渲染的场景(如大型表单、多维度筛选列表),避免全局状态更新导致的性能问题。

14. useEffectuseLayoutEffect 的区别是什么?

两者都是处理副作用的 Hooks,核心差异在“执行时机”和“用途”:

对比维度useEffectuseLayoutEffect
执行时机真实 DOM 更新后执行(异步),不阻塞浏览器渲染真实 DOM 更新前执行(同步),阻塞浏览器渲染
是否阻塞渲染不阻塞,浏览器先渲染页面,再执行 useEffect阻塞,需等 useLayoutEffect 执行完才渲染页面
适用场景大部分副作用(如数据请求、事件监听、日志上报),不依赖 DOM 最新状态需操作 DOM 并立即看到效果(如获取 DOM 尺寸后调整样式、避免页面闪烁)
示例发起接口请求获取列表数据渲染后立即获取 DOM 宽度,设置子组件位置

15. React 性能优化的常用方法有哪些?

性能优化是面试重点,需分“组件优化”“列表优化”“状态优化”等维度梳理:

  1. 组件层面优化

    • React.memo 包裹函数组件:对函数组件进行浅比较,若 Props 未变化,避免组件重渲染(如 const Child = React.memo(({ name }) => <div>{name}</div>))。
    • useMemo 缓存计算结果:避免每次渲染都执行昂贵的计算(如 const sortedList = useMemo(() => list.sort(), [list]),仅 list 变化时重新排序)。
    • useCallback 缓存函数:避免父组件渲染时生成新函数,导致子组件(React.memo 包裹)不必要重渲染(如 const handleClick = useCallback(() => {}, []))。
  2. 列表层面优化

    • 用唯一 key 优化列表:避免用 index 作为 key,减少 DOM 复用错误和不必要重渲染(见问题 9)。
    • 虚拟列表(Virtual List):处理长列表(如 1000+ 条数据)时,只渲染可视区域的元素,隐藏非可视区域元素(用 react-windowreact-virtualized 库),大幅减少 DOM 节点数量。
  3. 状态层面优化

    • 避免不必要的状态更新:只在数据变化时调用 setState,如输入框 onChange 中避免频繁更新(可配合防抖节流)。
    • 拆分状态粒度:将复杂状态拆分为多个独立状态,避免更新一个状态导致整个组件重渲染(如 const [name, setName] = useState(''); const [age, setAge] = useState(18),而非 const [user, setUser] = useState({ name: '', age: 18 }))。
  4. 其他优化

    • 懒加载组件:用 React.lazy + Suspense 实现组件懒加载,仅在组件需要渲染时才加载其 JS 代码(如路由组件:const Home = React.lazy(() => import('./Home'))),减少首屏加载时间。
    • 减少不必要的 DOM 操作:避免在 render 中创建 DOM 元素(如 document.createElement),尽量通过 JSX 描述 UI。
    • 生产环境构建:使用 create-react-appnpm run build 生成生产环境代码(去除开发环境调试代码、压缩 JS/CSS),减小包体积,提升加载速度。

16. React 表单处理的方式有哪些?(受控组件 vs 非受控组件)

表单处理是实战必备,需对比“受控”和“非受控”的核心差异:

(1)受控组件(Controlled Component)

  • 定义:表单元素(如 input、select)的值由 React State 完全控制,State 更新 → 表单值更新,表单值变化 → 触发事件同步 State。
  • 核心逻辑:通过 value 绑定 State,onChange 事件同步 State。
  • 示例(输入框)
    const [inputValue, setInputValue] = useState('');
    return (setInputValue(e.target.value)} // 同步 State
    />
    );
  • 优点:状态可控,易实现实时验证(如输入密码时提示强度)、表单联动(如选择省份后加载城市列表)、数据统一管理。
  • 缺点:需编写 onChange 事件,频繁更新 State(简单表单略繁琐)。
  • 适用场景:复杂表单(如注册表单、多字段联动表单),需实时反馈或数据校验。

(2)非受控组件(Uncontrolled Component)

  • 定义:表单元素的值由 DOM 自身控制,React 不通过 State 同步,而是通过 ref 获取 DOM 元素的值。
  • 核心逻辑:用 useRef 创建 ref 绑定表单元素,提交时通过 ref.current.value 获取值。
  • 示例(输入框)
    const inputRef = useRef(null);
    const handleSubmit = () => {
    console.log('输入值:', inputRef.current.value); // 获取值
    };
    return ({/* defaultValue 设初始值 */}
    );
  • 优点:代码简单,无需频繁更新 State,性能略好(减少渲染次数)。
  • 缺点:状态不可控,难实现实时验证,需操作 DOM(依赖 ref)。
  • 适用场景:简单表单(如登录表单、搜索框),无需实时反馈,仅提交时获取值。

17. 错误边界(Error Boundary)的作用及使用方法是什么?

错误边界是 React 处理组件错误的机制,需讲清“作用”“用法”和“限制”:

  • 作用:捕获子组件树中的 JavaScript 错误(如渲染错误、生命周期错误、子组件构造函数错误),防止错误导致整个应用崩溃,同时显示“备用 UI”(如“页面加载失败,请刷新”)。

  • 使用方法(仅类组件支持)

    1. 创建类组件,实现两个生命周期方法:
      • static getDerivedStateFromError(error):静态方法,接收错误对象,返回新 State(用于切换到备用 UI),无副作用(不能调用 setState 或请求数据)。
      • componentDidCatch(error, errorInfo):实例方法,接收错误和错误信息(如组件栈),用于错误日志上报(如发送到 Sentry),可包含副作用。
    2. 用该组件包裹可能出错的子组件。
  • 示例

    class ErrorBoundary extends React.Component {
    constructor(props) {
    super(props);
    this.state = { hasError: false }; // 初始无错误
    }
    // 错误发生后更新 State,显示备用 UI
    static getDerivedStateFromError(error) {
    return { hasError: true };
    }
    // 上报错误日志
    componentDidCatch(error, errorInfo) {
    console.error('组件错误:', error, errorInfo);
    // 上报到日志平台:fetch('/log', { method: 'POST', body: JSON.stringify({ error, errorInfo }) });
    }
    render() {
    if (this.state.hasError) {
    return 页面加载失败,请刷新重试; // 备用 UI
    }
    return this.props.children; // 无错误时渲染子组件
    }
    }
    // 使用:包裹可能出错的组件
    const App = () => ({/* 可能出错的组件 */}
    );
  • 注意事项(错误边界不能捕获的错误)

    1. 事件处理中的错误(如 onClick 回调中的错误)。
    2. 异步代码中的错误(如 setTimeoutfetchPromise 回调)。
    3. 服务端渲染(SSR)中的错误。
    4. 错误边界自身的错误(需外层错误边界捕获)。
    5. 函数组件不能直接作为错误边界(需用类组件包裹)。

18. React Router(v6)的核心概念及基础使用是什么?

React Router 是单页应用(SPA)路由的核心,v6 是当前主流版本,需讲清“核心概念”和“基础用法”:

(1)核心概念

  1. BrowserRouter / HashRouter:路由容器,包裹整个应用,决定路由模式:
    • BrowserRouter:基于 HTML5 History API(URL 无 #,如 https://xxx/home),需服务端配置支持(避免刷新 404)。
    • HashRouter:基于 URL 哈希(URL 含 #,如 https://xxx/#/home),无需服务端配置,兼容性好。
  2. Routes:替换 v5 的 Switch,用于包裹 Route 组件,只渲染匹配的第一个 Route(避免多个路由同时匹配)。
  3. Route:路由规则,定义“路径 → 组件”的映射:
    • path:URL 路径(如 /home/user/:id:id 是路由参数)。
    • element:路径匹配时渲染的组件(如 <Route path="/home" element={<Home />})。
  4. Link:导航组件,替换原生 <a> 标签,实现无刷新导航(避免页面重载),如 <Link to="/home">首页</Link>
  5. useNavigate:编程式导航钩子,替换 v5 的 useHistory,用于通过代码跳转(如登录后跳首页),如 const navigate = useNavigate(); navigate('/home');
  6. useParams:获取路由参数(如 /user/:id 中的 id),如 const { id } = useParams();
  7. useLocation:获取当前路由位置信息(如 pathnamesearchstate),如 const location = useLocation(); console.log(location.pathname);(当前路径)。

(2)基础使用步骤(v6)

  1. 安装npm install react-router-dom
  2. 配置路由容器:在根组件(如 index.js)用 BrowserRouter 包裹 App
    import { BrowserRouter } from 'react-router-dom';
    ReactDOM.createRoot(document.getElementById('root')).render(
    );
  3. 定义路由规则:在 App.js 中用 RoutesRoute 定义路由:
    import { Routes, Route, Link } from 'react-router-dom';
    import Home from './Home';
    import User from './User';
    import NotFound from './NotFound';
    const App = () => (
    {/* 导航栏 */}
    首页
    用户中心
    {/* 路由匹配 */}
    } /> {/* 首页 */}
    } /> {/* 用户中心(带参数) */}
    } /> {/* 404 页面 */}
    );
  4. 组件中使用路由钩子:在 User.js 中获取路由参数和导航:
    import { useParams, useNavigate } from 'react-router-dom';
    const User = () => {
    const { id } = useParams(); // 获取路由参数 id(123)
    const navigate = useNavigate(); // 编程式导航
    return (
    用户 ID:{id}
    );
    };

19. React 中如何处理异步请求(如接口调用)?

异步请求是实战核心,需讲清“常用方式”和“注意事项”:

(1)在 useEffect 中处理(最常用)

(2)用状态管理库处理(如 Redux)

  • 核心逻辑:将异步请求逻辑放在 Redux 的 action 中(用 redux-thunkredux-saga),请求状态(loading/data/error)存储在 store 中,组件通过 useSelector 获取状态,useDispatch 触发请求。
  • 适用场景:中大型应用,多组件需要共享异步请求的数据(如购物车列表、商品详情),便于统一管理和复用请求逻辑。

(3)自定义 Hooks 封装(复用请求逻辑)

(4)注意事项

  1. 处理 loadingerror 状态:避免用户看到空白页面或错误信息未提示,提升体验。
  2. 防止内存泄漏:用 AbortControllerfetch/axios 支持)或 clearTimeout 取消未完成的请求/定时器,避免组件卸载后请求成功触发 setState
  3. 避免重复请求:通过 useEffect 依赖项控制请求时机(如只在 url 变化时请求),或用缓存(如 SWR/React Query 库)优化。

20. 虚拟 DOM 的 Diff 算法原理是什么?(React 的 Reconciliation 过程)

Diff 算法是 React 高效渲染的核心,需解释“核心原则”和“执行过程”:

(1)核心设计原则(减少计算复杂度)

React Diff 算法基于两个假设,将时间复杂度从 O(n³)(全量对比)优化到 O(n)(线性对比):

  1. 同层比较:只对比 DOM 树的同一层级节点,不跨层级比较(如父节点和子节点不直接对比)。若某一层级节点类型变化,直接销毁该节点及所有子节点,重建新节点树(减少跨层级对比的成本)。
  2. 类型相同的组件:若两个组件类型相同(如都是 <User />),则认为其结构相似,继续对比其 Props 和子节点;若类型不同(如 <User /> 变为 <Product />),则销毁旧组件及子树,创建新组件及子树(避免复杂的内部对比)。
  3. 列表 key 唯一性:列表节点需用 key 标识唯一性,Diff 时通过 key 快速找到可复用的节点,仅进行移动、插入、删除操作(避免重新创建所有列表项)。

(2)Diff 执行过程(Reconciliation 调和阶段)

React 的渲染过程分为“调和阶段(Reconciliation)”和“提交阶段(Commit)”,Diff 算法在调和阶段执行:

  1. 生成新旧虚拟 DOM

  2. 对比新旧虚拟 DOM(Diff 核心)

  3. 生成更新队列:对比完成后,React 将所有差异(如属性变化、节点新增/删除/移动)整理成更新队列,传递到提交阶段。

  4. 提交阶段(Commit):React 根据更新队列,操作真实 DOM(只更新差异部分),并执行 componentDidMount/componentDidUpdate 等生命周期函数或 useEffect 清理/副作用逻辑。

21. React 18 的核心新特性有哪些?

React 18 是重要版本,需掌握核心特性(体现对 React 生态的关注):

  1. 并发渲染(Concurrent Rendering)

    • 定义:React 18 引入的核心特性,允许 React中断、暂停、恢复或放弃渲染,不再是“同步渲染到底”。
    • 作用:优先响应高优先级任务(如用户输入、点击),延迟低优先级任务(如列表筛选、数据加载),避免页面卡顿,提升用户体验(如用户输入时,列表渲染不会阻塞输入响应)。
    • 注意:并发渲染是“底层能力”,需通过高层 API(如 startTransition)触发。
  2. 自动批处理(Automatic Batching)

    • 定义:将多个 setState 更新合并为一次渲染,减少 DOM 操作次数(优化性能)。
    • 变化:React 17 及之前,仅在“合成事件”(如 onClick)和 useEffect 中支持批处理;React 18 中,所有场景(包括 setTimeoutPromise 回调、fetch 回调)都支持自动批处理。
    • 示例:
      // React 18 中,以下两个 setState 会合并为一次渲染
      setTimeout(() => {
      setCount(c => c + 1);
      setName('小明');
      }, 1000);
    • 取消批处理:用 ReactDOM.flushSync() 强制立即更新(如 ReactDOM.flushSync(() => setCount(c => c + 1)))。
  3. 过渡更新(Transitions)

    • 定义:区分“紧急更新”和“非紧急更新”,startTransition 标记的更新为“非紧急更新”,优先执行紧急更新(如用户输入)。
    • 作用:避免非紧急更新阻塞紧急更新,导致页面卡顿(如搜索框输入时,实时筛选列表是“非紧急更新”,用 startTransition 标记,确保输入流畅)。
    • 示例:
      import { startTransition } from 'react';
      const handleInput = (value) => {
      // 紧急更新:更新输入框值(优先执行)
      setInputValue(value);
      // 非紧急更新:筛选列表(延迟执行,不阻塞输入)
      startTransition(() => {
      setFilteredList(list.filter(item => item.includes(value)));
      });
      };
    • 配套 Hook:useTransition,返回 [isPending, startTransition]isPending 表示过渡更新是否正在进行(可显示加载状态)。
  4. Suspense 增强

    • 作用:Suspense 用于“等待异步操作完成后渲染组件”,React 18 扩展了其能力:
      • 支持服务端渲染(SSR)流式渲染:服务端可分段发送 HTML(先发送骨架屏,再发送异步组件内容),提升首屏加载体验。
      • 支持客户端异步数据加载:配合 use Hook(React 18 新增,用于读取 Promise),可在客户端用 Suspense 等待接口请求完成(如 const data = use(fetchData())Suspense 显示加载态)。
  5. useId Hook

    • 作用:生成跨服务端和客户端的唯一 ID,解决服务端渲染(SSR)中“客户端 ID 与服务端 ID 不匹配”的问题(如表单 label 的 for 属性、无障碍 ARIA 属性)。
    • 示例:
      import { useId } from 'react';
      const Input = () => {
      const id = useId(); // 服务端和客户端生成相同 ID
      return (
      用户名:
      );
      };

22. 自定义 Hooks 的定义、规则及示例是什么?

自定义 Hooks 是复用逻辑的核心,需讲清“定义”“规则”和“实战示例”:

(1)定义

自定义 Hooks 是封装可复用逻辑的函数,名称必须以 use 开头(React 识别 Hooks 的标识),内部可调用其他 React Hooks(如 useStateuseEffectuseContext),最终返回组件需要的数据或方法。

(2)核心规则(必须遵守,否则导致 Bug)

  1. 名称必须以 use 开头

    • 原因:React 通过名称识别 Hooks,确保 Hooks 规则(如不能在条件中调用)生效,若名称不以 use 开头,React 无法检测违规使用。
    • 错误示例:const fetchData = () => { ... }(应改为 const useFetch = () => { ... })。
  2. 只能在函数组件或其他自定义 Hooks 中调用

    • 禁止在 if-elsefor 循环、普通函数(非组件/非 Hooks)中调用,避免 Hooks 执行顺序混乱(React 依赖 Hooks 执行顺序维护状态)。
    • 错误示例:
      const Component = () => {
      if (isLogin) {
      const { data } = useFetch('/user'); // 错误:在 if 中调用 Hooks
      }
      };
  3. 可传递参数和返回值

    • 根据复用逻辑需求,自定义 Hooks 可接收参数(如请求 URL、配置项),返回组件需要的状态(如 data/loading/error)或方法(如 refresh 刷新函数)。

(3)实战示例:useLocalStorage(封装本地存储逻辑)

需求:封装“读取/写入 localStorage”的逻辑,支持组件同步 localStorage 数据(如记住用户名、主题设置)。

import { useState, useEffect } from 'react';
// 自定义 Hooks:封装 localStorage 逻辑
const useLocalStorage = (key, initialValue) => {
// 1. 初始化状态:优先从 localStorage 读取,无则用初始值
const [value, setValue] = useState(() => {
try {
const storedValue = localStorage.getItem(key);
return storedValue ? JSON.parse(storedValue) : initialValue;
} catch (err) {
// 处理异常(如 localStorage 被禁用)
console.error('读取 localStorage 失败:', err);
return initialValue;
}
});
// 2. 监听 value 变化,同步到 localStorage
useEffect(() => {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (err) {
console.error('写入 localStorage 失败:', err);
}
}, [key, value]); // 依赖 key 和 value,变化时同步
// 3. 返回 value 和更新方法(与 useState 用法一致)
return [value, setValue];
};
// 组件中使用
const LoginForm = () => {
// 用 useLocalStorage 替代 useState,自动同步 localStorage
const [username, setUsername] = useLocalStorage('username', '');
return (setUsername(e.target.value)}
placeholder="输入用户名"
/>
记住的用户名:{username}
);
};
  • 优势:组件无需重复编写 localStorage.getItem/setItem 逻辑,且数据自动同步,复用性强。

23. 什么是 Props Drilling(Props 透传)?如何解决?

Props Drilling 是实际开发中的常见问题,需解释“定义”和“解决方案”:

(1)定义

Props Drilling(Props 透传/Props 钻取)指:当组件层级较深时,父组件的 Props 需要经过中间多个“无关组件”层层传递,才能到达深层子组件的现象。中间组件本身不需要这些 Props,仅起到“传递”作用,导致代码冗余、维护成本高。

(2)解决方案(按场景选择)

  1. 使用 Context API

    • 适用场景:中小型应用,跨层级传递少量全局状态(如主题、用户信息)。
    • 原理:创建全局 Context,父组件用 Context.Provider 提供数据,深层子组件用 useContext 直接获取数据,跳过中间组件。
    • 优点:无需 Props 透传,代码简洁;无需引入额外库。
    • 缺点:Context 更新会触发所有消费组件重渲染(需配合 React.memo 优化);状态复杂时逻辑分散。
  2. 使用状态管理库

    • 适用场景:中大型应用,状态复杂且多组件共享(如购物车、订单状态)。
    • 方案:用 Redux、Zustand、Recoil 等库,将状态存储在全局 Store 中,任何组件(无论层级)都可直接从 Store 获取/修改状态,完全避免 Props 透传。
    • 优点:状态集中管理,便于调试和维护;支持复杂异步逻辑。
    • 缺点:小型应用成本高(如 Redux 配置繁琐)。
  3. 组件组合(Component Composition)

    • 适用场景:深层子组件与父组件存在直接逻辑关联,但中间层组件无需感知数据(如弹窗内的按钮需调用父组件的关闭方法)。
    • 原理:父组件直接将深层子组件作为 children 传递给中间组件,跳过 Props 透传(中间组件只需渲染 children 即可)。
    • 示例:
      // 父组件:直接传递子组件(含所需数据/方法)
      const App = () => {
      const [isOpen, setIsOpen] = useState(false);
      return {/* 中间组件 Modal 只需渲染 children */}setIsOpen(false)} /> {/* 深层子组件直接获取方法 */}
      ;
      };
      // 中间组件:无需接收/传递 onClose,仅渲染 children
      const Modal = ({ children }) => {children};
      // 深层子组件:直接使用父组件传递的 onClose
      const ModalContent = ({ onClose }) => ;
    • 优点:避免 Props 透传,逻辑更清晰;不依赖全局状态,适合局部组件通信。

24. useMemouseCallback 的作用及使用场景是什么?

两者均用于缓存,需明确“缓存内容”“使用场景”和“性能优化核心”:

(1)useMemo

(2)useCallback

  • 作用:缓存函数引用,避免组件每次渲染时创建新的函数实例(即使函数逻辑相同)。
  • 语法const memoizedCallback = useCallback(() => 函数逻辑, [依赖项数组])
    • 依赖项变化时,创建新函数并更新缓存;否则返回缓存的函数引用。
  • 示例
    // 缓存回调函数(仅 userId 变化时创建新函数)
    const handleDelete = useCallback((id) => {
    setList(list.filter(item => item.id !== id));
    }, [list]);
  • 适用场景
    • 函数作为 Props 传递给子组件,且子组件用 React.memo 优化(避免因函数引用变化导致子组件不必要重渲染)。
    • 函数作为 useEffect 的依赖项(避免因函数重新创建导致 useEffect 频繁执行)。

(3)核心注意事项

  • 不要过度使用:缓存本身有性能开销(存储和对比依赖项),简单计算或函数无需缓存(如 () => setCount(c + 1) 无需 useCallback)。
  • 依赖项必须完整useMemo/useCallback 内部使用的变量必须加入依赖数组,否则可能获取到过时的值(闭包陷阱)。

25. React 中如何实现组件的懒加载(代码分割)?

组件懒加载是优化首屏加载速度的关键,需讲清“核心 API”和“使用步骤”:

(1)核心原理

通过“代码分割(Code Splitting)”将应用代码拆分为多个小块(chunk),仅在组件需要渲染时才加载对应代码,减少首屏加载的 JS 体积,提升加载速度。

(2)实现方式(React.lazy + Suspense

  1. React.lazy:动态导入组件,返回一个“懒加载组件”(仅在首次渲染时加载组件代码)。

  2. Suspense:配合 React.lazy 使用,在懒加载组件加载完成前显示“加载占位符”(如骨架屏、加载动画),避免页面空白。

(3)实战示例(路由级懒加载)

路由组件通常是代码分割的最佳场景(用户不会同时访问所有页面):

import { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Loading from './Loading'; // 加载占位组件
// 懒加载路由组件(仅访问对应路由时加载代码)
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const User = lazy(() => import('./pages/User'));
const App = () => (
{/* Suspense 包裹所有懒加载组件,统一显示加载态 */}
}>
} />
} />
} />
);

(4)注意事项

  • 错误处理:懒加载可能失败(如网络错误),需用错误边界(Error Boundary) 捕获加载错误,显示友好提示(如“页面加载失败,请重试”)。
  • 服务端渲染(SSR)React.lazy 不支持服务端渲染,需用 loadable-components 等库替代。
  • 拆分粒度:避免过度拆分(如每个组件拆分为单独 chunk),可能导致请求数量过多;建议按路由或功能模块拆分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/909252.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

asp.net中的wwwroot是什么

wwwroot 是 ASP.NET Core 的**“Web根目录”,也叫“静态文件根目录”。它只是一个约定俗成的文件夹名**,你可以改成任何名字,甚至挪到磁盘外面,但默认就叫 wwwroot。 一句话:把浏览器能直接下载到的静态文件(htm…

用光学计算加速AI模型中的卷积和矩阵乘法操作

本文档深入探讨了如何利用光学原理进行高效计算,特别是针对现代AI模型中常见的卷积和矩阵乘法操作。内容涵盖了从加速卷积的深层数学基础,到实现这些计算的各种前沿光学器件,最后对光学加速器与传统GPU进行了系统级…

了解IWebHostEnvironment : IHostEnvironment

IWebHostEnvironment : IHostEnvironment 是 C# 中的接口继承语法,表示:IWebHostEnvironment 接口继承自 IHostEnvironment 接口。✅ 一句话解释: IWebHostEnvironment 是 ASP.NET Core 中专门用于Web应用的环境信息…

PDF24 Creator(完全免费多功能PDF工具箱) 易于使用 多语言支持 - 教程

PDF24 Creator(完全免费多功能PDF工具箱) 易于使用 多语言支持 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &q…

彩笔运维勇闯机器学习--lasso回归

前言 彩笔运维勇闯机器学习,今天我们来讨论一下lasso回归,本期又是一起数学推理过程展示 坐标下降法 目标找到一组参数,使目标函数值最小。比如\(f(x,y)=3x^2+5xy+10y^2\),要找到\(x,y\)使得\(f(x,y)\)取值最小 \[…

IP地址的配置

原IP地址为172.16.69.251

工业检测为啥首选黑白相机?4 个核心优势,彩色相机比不了 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

202504_CHIMA模拟_Shiro流量分析

流量分析, WebShell, ShiroTags:流量分析,WebShell,Shiro 0x00. 题目 题目表述 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附件 附件名称:202504_CHIMA_analyse 0x01. WP …

vscode彻底删除安装过的插件和个人配置信息

前言 今天上班打开电脑发现以前修改的vscode背景图片不见了,配置改了跟多次,也卸载重装很多次,都没用,最后检查了一下发现是个人配置没有删掉1、先到电脑设置里面把vscode卸载,虽然卸载了但是用户的配置信息还会在…

船舶运动控制,PID控制算法,反步积分控制器

一、控制架构设计 控制器协同机制PID层:处理常规跟踪误差(位置/航向偏差) 反步积分层:补偿模型不确定性和高频扰动 前馈补偿:抵消风浪流等确定性干扰二、PID控制算法实现 1. 标准PID结构 % 经典PID控制器代码(增…

光隔离探头与高压差分探头的可替代性讨论

一、原理 光隔离探头主要依靠光电转换原理工作。如图1所示,当探头检测到线路中的电流或电压信号时,这些电信号会通过电光转换器转化为光信号。光信号通过光纤传输,随后通过光电还原器重新转化为电信号,最终输入到示…

冰箱 EMC 测试中 RE 超标?近场探头定位干扰源实操指南

在现代科技的快速发展中,电子设备的广泛应用使得电磁兼容性(EMC)问题日益突出。对于像冰箱这样的常见家电产品,EMC测试显得尤为重要,其中辐射发射(RE)测试是评估其电磁兼容性的关键环节。当冰箱在RE测试中出现超…

【笔记】人工智能原理

【笔记】人工智能原理,

【通达信公式性能优化】:高级技巧揭秘,提升执行效率的10大策略 - Leone

目录摘要 关键字 1. 通达信公式性能优化概论1.1 为什么要进行通达信公式优化 1.2 优化的目标与方法概述2. 理解通达信公式的执行机制公式的基本组成与执行流程公式元素解析 数据处理流程公式性能的基本评估方法评估指标…

HTTPS 映射如何做?(HTTPS 映射配置、SNI 映射、TLS 终止、内网映射与 iOS 真机验证实战)

本文详解 HTTPS 映射方案(TLS 终止、SNI 映射、TLS 透传)、NGINX/HAProxy 配置要点、常见故障排查与证书管理,并介绍如何在 iOS 真机上用抓包大师(Sniffmaster)验证映射与 TLS 握手,便于快速定位问题。在生产与开…

STM32 FreeRTOS + LwIP 集成实践:基于 MQTT 的通信示例 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

数分3

回忆: \[\]

基于模拟退火算法解决带容量限制车辆路径问题(CVRP)的MATLAB实现

一、问题建模 1.1 数学描述 目标函数:其中:\(R_k\):第k条路径的边集合 \(dij\):节点i到j的距离 \(K\):使用车辆数 \(λ\):车辆使用惩罚系数约束条件:每个客户仅被访问一次 车辆从仓库出发并返回 路径载重不超过…

完整教程:分片后的聚合分页处理

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:HDR 动态元数据生成:场景自适应与质检脚本

深入解析:HDR 动态元数据生成:场景自适应与质检脚本pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…