面试备战录

153 阅读4分钟

1、React.lazy 与 Suspense 如何实现组件懒加载?

答:React.lazy + SuspenseReact内置的代码分割 & 懒加载方案

  • React.lazy:让你把组件按需异步加载,而不是一次性打包进主bundle;本质上是import()的语法糖。
  • React.Suspense:用来指定 加载中状态(fallback 的占位UI
import React, { Suspense } from 'react';
// 懒加载组件
const About = React.lazy(() => import('./About'));
function App(){
  return(
  <div>
    <h1>首页</h1>
    <Suspense fallback={<div>加载中...</div>}>
      <About />
    </Suspense>
  </div>
  )
}

执行流程:

  • 首次渲染时,About组件不会打包进主bundle
  • React渲染到<About />时,触发import("./About") → 异步加载
  • 加载过程中,Suspense显示fallback
  • 加载完成后,React渲染About组件

原理:

  • React.lazy接收一个函数,这个函数返回一个Promise(动态import模块)
  • React内部会把这个异步组件包装成一个“占位符”
  • Promise pending → 渲染Suspense.fallback
  • Promise resolved → 渲染真实组件
  • Promise rejected → 可以配合ErrorBoundary兜底

注:

  • 必须在Suspense里使用,否则报错
  • 不支持命名导出 → 懒加载的模块必须是默认导出
export default function About() { ... } ✅
export function About() { ... } ❌
  • SSR(Next.js)不能直接用,需要框架自己的动态导入方案(next/dynamic

2、自定义 hook 如何实现?有哪些使用场景?

答:自定义HookReact提供的一种逻辑复用机制,它不会产生新的语法或生命周期,只是让我们把stateful逻辑抽离出来。它的优势是复用、解耦、增强可维护性,常用于数据请求、事件监听、表单处理等场景。

  • 自定义 Hook:就是把多个组件中复用的状态逻辑(state、副作用、计算等) 抽离成一个函数,以use开头,内部可以使用其他hooks
  • 使用场景:逻辑复用(如表单处理、数据请求、事件监听)、代码解耦、提升可维护性。
  • React官方规定 自定义Hook必须以use开头,这样才能让React正确跟踪hook调用顺序。
  • 自定义Hook其实就是一个函数,里面可以调用useState、useEffect、useMemo等。
  • 不会改变 React 的渲染逻辑,只是复用逻辑而非复用状态。

3、React 中如何实现组件间通信?(Context、props、event、bus)

答:React组件通信常见方式包括:props(父子)、props+回调(子父)、Context(跨层级)、事件总线(任意组件)、以及全局状态管理库(复杂场景)。通常小型项目用props/Context就够了,大型项目推荐使用Redux、Zustand 或 Recoil来管理状态。

  • props(父 → 子):父组件通过props传递数据和回调给子组件。
    • 优点:简单直观,类型安全(配合TS)。
    • 缺点:多层组件传递(prop drilling)会导致冗余。
  • props + 回调 (子 → 父):父组件传递一个回调函数给子组件,子组件触发回调传值给父组件。
    • 优点:逻辑清晰。
    • 缺点:仍然受限于父子关系,兄弟组件需要提升到共同父级。
  • Context(跨层级):通过React.createContext创建全局上下文,避免props drilling
    • 优点:适合全局配置(主题、语言、用户信息)。
    • 缺点:Context 值更新时,所有消费它的组件都会重新渲染(需配合use-context-selector或分片Context优化)。
  • 事件总线(Event Bus)(任意组件间):实现一个简单的发布-订阅系统,非父子关系也能通信。
    • 优点:解耦,组件间关系不受限制。
    • 缺点:全局副作用,不利于维护和调试;适合轻量场景,不推荐大规模使用。
// eventBus.ts
import mitt from 'mitt';
export const bus = mitt();
// A.tsx
bus.emit('send', 'Hello B');
// B.tsx
bus.on('send', msg => console.log(msg));
  • 状态管理库(Redux / Zustand / Recoil):将状态提升到全局Store,组件通过hooks订阅和更新。
    • 优点:大规模项目首选,支持调试、持久化、异步请求。
    • 缺点:增加学习和引入成本。

4、如何在 React 中优雅地处理请求状态?(如 loading / error)

  • 本地状态管理:用useState来管理请求的三个状态
    • 优点:简单直观
    • 缺点:每个组件都要写一遍loading/errorw,重复代码多
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
  • 封装自定义 Hook
    • 优点:逻辑复用,简洁
    • 缺点:功能有限,需要自己维护缓存 / 重试
function useFetch(url){
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(()=>{
    let active = true;
    setLoading(true);
    fetch(url)
        .then(res => res.json())
        .then(data => active && setData(data))
        .catch(err => active && setError(err))
        .finally(() => active && setLoading(false));
    return () => {
        active = false; 
    }  // 避免竞态更新

},[url])
return { data, loading, error };
}
// 使用 
const { data, loading, error } = useFetch(`/api/user/${userId}`);
  • 第三方库
    • React Query / SWR / Apollo Client
    • 缓存、去重、自动重试
    • 状态管理(loading/error/success
    • 数据过期和重新请求
    • 优点:几乎不用操心请求状态,专注渲染
    • 缺点:引入库,学习成本
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
    const { data, isLoading, isError } = useQuery({
        queryKey: ['user', userId],
        queryFn: () => fetch(`/api/user/${userId}`).then(res => res.json())
    })
    if (isLoading) return <p>加载中...</p>;
    if (isError) return <p>请求失败</p>;
    return <div>{data.name}</div>;
}