React面试题及详细答案150道(61-70)

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

61. 什么是 React.lazy 和 Suspense?它们的作用是什么?

答案:
React.lazy 和 Suspense 是 React 16.6 引入的特性,用于实现组件的懒加载(Code Splitting)。

  • React.lazy: 允许你动态导入组件,使组件在需要时才加载。
  • Suspense: 用于包裹懒加载的组件,在组件加载完成前显示加载状态(如加载动画)。

原理:
懒加载通过 Webpack 等打包工具将代码分割成多个小块,减少初始加载时间。Suspense 则处理加载过程中的状态管理。

示例代码:

// 懒加载组件
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      <React.Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </React.Suspense>
    </div>
  );
}

62. 什么是React的Context API?如何避免性能问题?

Context API 用于跨层级传递数据,避免props drilling。

性能优化

  1. 拆分Context

    const UserContext = React.createContext();
    const ThemeContext = React.createContext();
    
    <UserContext.Provider value={user}>
      <ThemeContext.Provider value={theme}>
        <App />
      </ThemeContext.Provider>
    </UserContext.Provider>
    
  2. 使用useMemo缓存值

    const contextValue = useMemo(() => ({ user, setUser }), [user]);
    <UserContext.Provider value={contextValue}>
      {children}
    </UserContext.Provider>
    
  3. 避免深层嵌套

    // 不好的写法
    <Context.Provider value={value}>
      <DeeplyNestedComponent />
    </Context.Provider>
    
    // 优化:将Provider靠近消费组件
    <ParentComponent>
      <Context.Provider value={value}>
        <ChildComponent />
      </Context.Provider>
    </ParentComponent>
    

63. 如何在React中实现表单验证?

表单验证方案

  1. 受控组件 + 状态管理

    const [formData, setFormData] = useState({ name: '', email: '' });
    const [errors, setErrors] = useState({});
    
    const validate = () => {
      let tempErrors = {};
      if (!formData.name) tempErrors.name = 'Name is required';
      if (!formData.email) tempErrors.email = 'Email is required';
      return Object.keys(tempErrors).length === 0 ? null : tempErrors;
    };
    
    const handleSubmit = (e) => {
      e.preventDefault();
      const validationErrors = validate();
      if (validationErrors) {
        setErrors(validationErrors);
      } else {
        // 提交表单
      }
    };
    
    return (
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={formData.name}
          onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        />
        {errors.name && <span>{errors.name}</span>}
      </form>
    );
    
  2. 使用第三方库(Formik + Yup)

    import { Formik, Form, Field, ErrorMessage } from 'formik';
    import * as Yup from 'yup';
    
    const validationSchema = Yup.object({
      name: Yup.string().required('Required'),
      email: Yup.string().email('Invalid email').required('Required'),
    });
    
    return (
      <Formik
        initialValues={{ name: '', email: '' }}
        validationSchema={validationSchema}
        onSubmit={handleSubmit}
      >
        <Form>
          <Field name="name" />
          <ErrorMessage name="name" component="span" />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    );
    

64. 如何在React中实现文件上传?

文件上传实现

  1. 基本表单上传

    const [selectedFile, setSelectedFile] = useState(null);
    
    const handleFileChange = (e) => {
      setSelectedFile(e.target.files[0]);
    };
    
    const handleUpload = () => {
      const formData = new FormData();
      formData.append('file', selectedFile);
    
      fetch('/api/upload', {
        method: 'POST',
        body: formData,
      })
        .then((response) => response.json())
        .then((data) => console.log(data));
    };
    
    return (
      <div>
        <input type="file" onChange={handleFileChange} />
        <button onClick={handleUpload}>Upload</button>
      </div>
    );
    
  2. 多文件上传

    <input type="file" multiple onChange={handleFileChange} />
    
    const handleFileChange = (e) => {
      const files = Array.from(e.target.files);
      files.forEach((file) => {
        // 处理每个文件
      });
    };
    
  3. 上传进度显示

    const [progress, setProgress] = useState(0);
    
    const uploadFile = (file) => {
      const xhr = new XMLHttpRequest();
      xhr.upload.onprogress = (e) => {
        if (e.lengthComputable) {
          setProgress((e.loaded / e.total) * 100);
        }
      };
      xhr.open('POST', '/api/upload');
      xhr.send(file);
    };
    

65. 如何优化 React 应用的性能?请列举至少 3 种方法。

答案:

  1. 使用 shouldComponentUpdate 或 React.memo:

    • shouldComponentUpdate(类组件):通过比较 props/state 决定是否重新渲染。
    • React.memo(函数组件):浅比较 props,避免不必要的渲染。
  2. 虚拟列表(Virtualized List):
    只渲染可视区域的列表项,适用于大数据量列表(如 react-window 或 react-virtualized)。

  3. 懒加载(Code Splitting):
    使用 React.lazy 和 Suspense 分割代码,减少初始加载时间。

  4. 使用 useMemo 和 useCallback:

    • useMemo:缓存计算结果,避免重复计算。
    • useCallback:缓存函数引用,避免子组件因 props 变化而重新渲染。

示例代码:

// 使用 React.memo
const MemoizedComponent = React.memo(({ data }) => {
  return <div>{data}</div>;
});

// 使用 useMemo 缓存计算结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

66. 什么是 Context API?如何避免 Context 导致的组件重新渲染?

答案:
Context API 允许你在组件树中共享数据,避免通过 props 层层传递(“prop drilling”)。

避免重新渲染的方法:

  1. 拆分 Context: 将高频变化的数据和低频变化的数据分离到不同的 Context 中。
  2. 使用 useMemo 包裹 Provider: 避免 Provider 的 value 引用变化。
  3. 将 Consumer 组件提取到单独文件: 减少父组件更新对 Consumer 的影响。

示例代码:

// 创建 Context
const UserContext = React.createContext();

// 提供者组件
function UserProvider({ children }) {
  const user = useContextValue(); // 假设这是一个自定义 Hook
  const memoizedValue = useMemo(() => ({ user }), [user]);
  
  return (
    <UserContext.Provider value={memoizedValue}>
      {children}
    </UserContext.Provider>
  );
}

67. 什么是 React 的合成事件(SyntheticEvent)?它的工作原理是什么?

答案:
合成事件是 React 封装的跨浏览器兼容的事件系统,统一了不同浏览器的事件接口(如 onClick、onChange)。

工作原理:

  1. 事件委托(Event Delegation): React 将所有事件绑定到文档根节点(document)。
  2. 事件对象包装: 将原生事件包装成合成事件对象,提供一致的 API(如 e.preventDefault())。
  3. 事件池(Event Pooling): 复用事件对象以提高性能(React 17 已移除)。

示例代码:

function Button({ onClick }) {
  return (
    <button onClick={(e) => {
      e.preventDefault(); // 合成事件方法
      onClick();
    }}>
      Click me
    </button>
  );
}

68. 什么是 React Fiber?它解决了什么问题?

答案:
React Fiber 是 React 16.x 版本引入的协调算法(Reconciliation)的重新实现。

主要改进:

  1. 任务分片(Task Slicing): 将渲染任务分成多个小任务,允许中断和恢复。
  2. 优先级调度: 高优先级任务(如动画)可插队执行。
  3. 更好的用户体验: 避免长时间阻塞主线程,减少页面卡顿。

解决的问题:
在大型应用中,旧的协调算法可能导致长时间的渲染阻塞,影响用户体验。Fiber 通过增量渲染(Incremental Rendering)解决了这个问题。

69. 如何在React中实现单元测试?

单元测试实现

  1. 使用Jest + React Testing Library

    // 组件
    function Button({ onClick, text }) {
      return <button onClick={onClick}>{text}</button>;
    }
    
    // 测试
    import { render, fireEvent } from '@testing-library/react';
    
    test('Button click triggers callback', () => {
      const mockClick = jest.fn();
      const { getByText } = render(<Button onClick={mockClick} text="Click me" />);
      
      fireEvent.click(getByText('Click me'));
      
      expect(mockClick).toHaveBeenCalledTimes(1);
    });
    
  2. 测试异步操作

    test('Fetches data on button click', async () => {
      const mockFetch = jest.fn(() =>
        Promise.resolve({ json: () => Promise.resolve({ name: 'Test' }) })
      );
      global.fetch = mockFetch;
    
      const { getByText } = render(<FetchButton />);
      fireEvent.click(getByText('Fetch'));
    
      await waitFor(() => expect(getByText('Test')).toBeInTheDocument());
    });
    
  3. 测试状态更新

    test('Counter increments on click', () => {
      const { getByText } = render(<Counter />);
      const button = getByText('0');
      
      fireEvent.click(button);
      expect(getByText('1')).toBeInTheDocument();
    });
    

70. 如何在 React 中实现路由?

答案:
React 本身不提供路由功能,通常使用第三方库如 react-router-dom

主要概念:

  • BrowserRouter:使用 HTML5 History API 实现路由。
  • RoutesRoute:定义路由匹配规则。
  • Link:导航链接。
  • useNavigate:编程式导航。

示例代码:

import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还是大剑师兰特

打赏一杯可口可乐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值