react面试题整理

目录

  • 1. 什么是 React?它有哪些核心特性?
  • 2. 什么是 JSX?为何使用?
  • 3. React 中的组件有哪些类型?
  • 4. 什么是 Hook?你用过哪些?
  • 5. useEffect 的作用是什么?
  • 6. 如何实现列表渲染与 Key 的作用?
  • 7. 什么是受控组件与非受控组件?
  • 8. 事件绑定与事件对象
  • 9. 如何优化 React 性能?
  • 10. React 路由(react-router-dom)基本用法?
  • 11. 什么是 Redux?如何与 React 结合使用?
  • 12. 什么是 Context?用来解决什么问题?
  • 13. React 中如何实现组件通信?
  • 14. 什么是虚拟 DOM?它的优点?
  • 15. React 与 Vue 的对比
  • 16. Umi 路由配置与动态路由
  • 17. React 性能优化常见技巧
  • 18. 数据可视化大屏开发的性能优化
  • 19. React 中的错误边界是什么?如何使用?
  • 20. React 中的高阶组件(HOC)是什么?有什么用途?

1. 什么是 React?它有哪些核心特性?

React 是一个用于构建用户界面的 JavaScript 库,主要用于构建单页应用(SPA)。由 Facebook 维护。它的核心思想是将 UI 拆分成多个小的、可复用的组件,通过组合这些组件来构建复杂的界面。

核心特性:

组件化开发(Component-Based):将页面拆分成多个独立的组件,每个组件负责一个特定的功能或 UI 部分,提高代码的可维护性和复用性。

虚拟 DOM(Virtual DOM):是 React 使用的轻量级 JavaScript 对象,用来描述 DOM 结构。React 通过对比新旧虚拟 DOM 的差异,只更新需要更新的真实 DOM 节点,减少直接操作 DOM 的次数,提高性能。

单向数据流(One-way Data Flow):数据的流动是单向的,使得数据的流向更加清晰,便于调试和维护。

声明式编程(Declarative UI):通过描述 UI 的最终状态,而不是如何去实现这个状态,让代码更加简洁和易于理解。

JSX 语法扩展:允许在 JavaScript 代码中写 HTML,使代码更直观,便于理解 UI 结构,同时支持变量、函数表达式。

const Hello = () => <h1>Hello, React!</h1>;

2. 什么是 JSX?为何使用?

JSX 是 JavaScript 的语法扩展,允许你在 JavaScript 代码中写 HTML。它并不是一种新的语言,而是一种语法糖,最终会被 Babel 转译为 React.createElement ()。

好处:

  • 代码更直观:可以直观地看到 UI 的结构,降低了代码的理解成本。
  • 支持变量和表达式:可以在 JSX 中使用 JavaScript 变量和表达式,使代码更加灵活。
const name = '小明';
const greet = <h1>你好,{name}</h1>;

3. React 中的组件有哪些类型?

1、函数组件(Function Component) ✅ 推荐

  • 函数组件是一种纯函数,它接收 props 作为参数,并返回一个 React 元素。函数组件没有自己的状态和生命周期方法,代码简洁,易于理解和测试。
  • 示例:
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

4. 什么是 Hook?你用过哪些?

Hook 是 React 16.8 引入的特性,使函数组件具备状态管理和副作用处理的能力,解决了类组件的一些问题,如代码复用困难、状态逻辑复杂等。

- useState:声明状态,允许在函数组件中使用状态。
- import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>点击次数:{count}</p>
      <button onClick={() => setCount(count + 1)}>点击我</button>
    </div>
  );
}
  • useEffect:副作用处理(如请求数据、订阅事件等),类似于生命周期函数。
useEffect(() => {
  console.log("组件挂载");
  return () => console.log("组件卸载");
}, []);
- useRef:引用dom或者保存一些可变值,在组件的整个生命周期内保持不变
import { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>聚焦输入框</button>
    </>
  );
}
  • useMemo / useCallback:性能优化,useMemo 用于缓存计算结果,useCallback 用于缓存函数。
import { useMemo, useCallback } from 'react';

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

5. useEffect 的作用是什么?

useEffect 处理副作用逻辑,类似于生命周期函数。它可以在组件渲染后执行一些操作,如数据获取、订阅事件、手动修改 DOM 等。

  • 没有依赖:每次渲染都触发,类似于 componentDidMount 和 componentDidUpdate。
useEffect(() => {
  console.log("组件渲染");
});

useEffect(() => {
  console.log("组件渲染");
  return{
	//componentWillUnmount
}
});

  • 空数组:只在初次渲染时触发(componentDidMount)。
useEffect(() => {
  console.log("组件挂载");
  return () => console.log("组件卸载");
}, []);
  • 有依赖:依赖变化时触发(componentDidUpdate)。
useEffect(() => {
  console.log("依赖变化");
}, [dependency]);

总结对比表

依赖数组 执行时机 适用场景 风险提示
不传(undefined) 每次渲染(包括首次和更新) 无依赖的副作用(如强制刷新)类似于 componentDidMount 和 componentDidUpdate。 可能导致性能问题
空数组([]) 仅挂载和卸载时执行一次 初始化操作、订阅、清理副作用类似(componentDidMount)。 需确保无隐式依赖
非空数组 依赖项变化时执行 同步数据变化的副作用(如网络请求) 类似于(componentDidUpdate) 需完整声明所有依赖

6. 如何实现列表渲染与 Key 的作用?

React 使用 map() 方法来渲染数组。key 是一个特殊的属性,它帮助 React 标识哪些元素更改、添加或删除,从而提高渲染性能。

const list = ['a', 'b', 'c'];
<ul>
  {list.map((item, index) => (
    <li key={index}>{item}</li>
  ))}
</ul>

7. 什么是受控组件与非受控组件?

  • 受控组件:值受 state 控制,推荐使用。每次输入变化时,都会触发 onChange 事件,更新 state 中的值。
import { useState } from 'react';

function ControlledInput() {
  const [value, setValue] = useState('');
  return (
    <input value={value} onChange={e => setValue(e.target.value)} />
  );
}
  • 非受控组件 用 ref 获取 DOM 值,不需要在每次输入变化时更新 state
import { useRef } from 'react';

function UncontrolledInput() {
  const inputRef = useRef(null);
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(inputRef.current.value);
  };
  return (
    <form onSubmit={handleSubmit}>
      <input ref={inputRef} type="text" />
      <button type="submit">提交</button>
    </form>
  );
}

8. 事件绑定与事件对象

React 事件命名采用小驼峰命名法,例如 onClick。事件处理函数通过属性传递给组件,事件对象是合成事件,它在不同浏览器中具有一致的接口。

function handleClick(e) {
  console.log('点击事件触发', e);
}

<button onClick={(e) => handleClick(e)}>点我</button>

9. 如何优化 React 性能?

  • 使用 React.memo:缓存组件,避免无意义重新渲染,适用于函数组件。
    react,memo 是react 提供的一个高阶组件(Hoc),用于韩村函数组件的渲染结果,当组件的props未发生变化时,reactmemo会直接服用上一次的渲染结果,而非成型执行组件函数,从而避免无意义的重新渲染,提高性能
const MyComponent = React.memo((props) => {
  return <div>{props.value}</div>;
});

适用场景
1、纯展示型组件

组件仅依赖传入的 props,无内部状态(useState)或副作用(useEffect),且无需频繁更新。

const Display = React.memo(({ text }) => {
  console.log('Render:', text);
  return <div>{text}</div>;
});

2、列表项组件
在长列表中,使用react。memo可避免音符组件重新渲染导致的列表项重复渲染


```javascript
const ListItem = React.memo(({ item }) => {
  return <div>{item.name}</div>;
});

3、需要优化的复杂计算组件
组件包含复杂的逻辑或计算,但仅依赖稳定的props,可通过react。memo缓存结果

**import React, { useState } from 'react';
// 定义函数组件
const MyComponent = ({ name }) => {
  console.log('Component rendered');
  return <div>{name}</div>;
};
// 使用 React.memo 包装组件
const MemoizedComponent = React.memo(MyComponent);
// 或直接内联使用
export default function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <MemoizedComponent name="Alice" />
      <button onClick={() => setCount(count + 1)}>Rerender Parent</button>
    </>
  );
}**
  • useMemo, useCallback:缓存结果或函数,避免不必要的计算和函数创建。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

10. React 路由(react-router-dom)基本用法?

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

function Home() {
  return <h1>主页</h1>;
}

function About() {
  return <h1>关于页面</h1>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

11. 什么是 Redux?如何与 React 结合使用?

Redux 是一个状态管理库,适合在中大型应用中使用。它采用单向数据流的设计思想,将应用的所有状态集中管理,使得状态的变化可预测和可追溯。
核心概念:

  • Store:存储应用的所有状态。
  • Action:描述状态的变化,是一个包含 type 属性的对象。
  • Reducer:是一个纯函数,根据 action 来更新状态

结合 React 示例:

- // store.js
import { createStore } from 'redux';

const reducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case 'INCREMENT': return { count: state.count + 1 };
    default: return state;
  }
};
const store = createStore(reducer);

// App.js
import { Provider, useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>加一</button>
    </div>
  );
}

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

12. 什么是 Context?用来解决什么问题?

Context 用于解决多层组件传值(props drilling)的问题,允许在组件树中共享数据,而不必显式地通过每个组件传递 props。类似于vue 中的provide/inject(跨层级组件传值)

示例:

**import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}
function Toolbar() {
  return <ThemedButton />;
}
function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>按钮</button>;
}**

13. React 中如何实现组件通信?

  • 父子通信:通过 props 传递数据,父组件将数据作为 props 传递给子组件。
function Parent() {
  const message = 'Hello from parent';
  return <Child message={message} />;
}

function Child(props) {
  return <p>{props.message}</p>;
}
  • 子传父:通过 props 传递回调函数,子组件调用回调函数将数据传递给父组件。
function Parent() {
  const handleChildData = (data) => {
    console.log('Received data from child:', data);
  };
  return <Child onData={handleChildData} />;
}

function Child(props) {
  const sendData = () => {
    props.onData('Data from child');
  };
  return <button onClick={sendData}>发送数据给父组件</button>;
}
  • 兄弟组件:提升状态到共同的父组件,或者使用 Redux 或 Context 进行状态管理。
  • 跨层通信:使用 Context 直接在不同层级的组件之间共享数据。
  • 跨组件:使用事件总线或状态管理方案,如 Redux、MobX 等。

14. 什么是虚拟 DOM?它的优点?

虚拟 DOM 是 React 使用的轻量级 JavaScript 对象,用来描述 DOM 结构。它是真实 DOM 的抽象表示,React 通过对比新旧虚拟 DOM 的差异,只更新需要更新的真实 DOM 节点。
优点:

  • 减少直接操作 DOM 的次数:直接操作 DOM 是比较昂贵的操作,虚拟 DOM 通过批量更新和差异比较,减少了对真实 DOM 的操作次数,提高了性能。
  • 提高性能:通过 diff 算法,只更新发生变化的部分,避免了不必要的渲染。
  • 实现跨平台渲染:由于虚拟 DOM 是一个 JavaScript 对象,它可以在不同的平台上进行渲染,如 React Native 可以将虚拟 DOM 渲染为原生组件。

15. React 与 Vue 的对比

特性 React Vue
数据绑定 单向数据流 双向数据绑定(v-model)
编程范式 函数式(Hooks)、JSX 模板驱动、选项式(Vue 3 支持组合式)
虚拟 DOM
状态管理 Redux、Context、Zustand 等 Vuex、Pinia
生态工具 Next.js、Umi、Remix Nuxt.js
学习曲线 高(需要理解 JSX、Hooks 等) 相对较低(上手快)

16. Umi 路由配置与动态路由

Umi 提供约定式与配置式路由:

✅ 约定式路由

项目中 pages/ 目录结构自动映射成路由。

pages/
├── index.tsx => /
├── user.tsx => /user

✅ 配置式路由
在 config/config.ts 中配置:

export default {
  routes: [
    { path: '/', component: '@/pages/index' },
    { path: '/user', component: '@/pages/user' },
  ],
};

17. React 性能优化常见技巧

技术 / 方法 说明
React.memo 缓存组件,避免无意义重新渲染
useMemo/useCallback 缓存函数或计算结果
shouldComponentUpdate / PureComponent 控制类组件更新
虚拟滚动 react-window / react-virtualized
Profiler React DevTools 中性能分析工具
图片优化 lazyload、webp、CDN

18. 数据可视化大屏开发的性能优化

可视化项目(如 ECharts/AntV)性能瓶颈常在于渲染与数据量:
✅ 技术层面

  • 使用 canvas / webgl 替代 svg 渲染(如 ECharts 默认采用 canvas),提高渲染性能。
  • 降低数据粒度,只展示关键数据,减少数据处理量。
  • 定时增量更新而非全量刷新图表,减少不必要的渲染。
  • 图表初始化使用 echarts.init 缓存实例,避免重复初始化。
    ✅ 示例代码
import { useEffect } from 'react';
import * as echarts from 'echarts';

function ChartComponent() {
  useEffect(() => {
    const chart = echarts.init(document.getElementById('main'));
    const option = {
      // 图表配置
    };
    chart.setOption(option);
    const resizeObserver = () => chart.resize();
    window.addEventListener('resize', resizeObserver);
    return () => {
      window.removeEventListener('resize', resizeObserver);
      chart.dispose();
    };
  }, []);

  return <div id="main" style={{ width: '600px', height: '400px' }}></div>;
}

19. React 中的错误边界是什么?如何使用?

错误边界是 React 16 引入的一个特性,用于捕获并处理子组件树中的 JavaScript 错误,防止错误导致整个应用崩溃。错误边界是一个类组件,它定义了 componentDidCatch 或 getDerivedStateFromError 生命周期方法。

示例:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, errorInfo) {
    // 可以在这里记录错误日志
    console.log(error, errorInfo);
    this.setState({ hasError: true });
  }

  static getDerivedStateFromError(error) {
    // 更新 state 以显示错误 UI
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>发生错误,请稍后重试。</h1>;
    }
    return this.props.children;
  }
}

function BuggyComponent() {
  throw new Error('模拟错误');
  return <div>这是一个有错误的组件</div>;
}

function App() {
  return (
    <div>
      <ErrorBoundary>
        <BuggyComponent />
      </ErrorBoundary>
    </div>
  );
}

20. React 中的高阶组件(HOC)是什么?有什么用途?

高阶组件(Higher-Order Component,简称 HOC)是一个函数,它接收一个组件作为参数,并返回一个新的组件。高阶组件是 React 中复用代码和逻辑的一种方式。

用途:

  • 代码复用:可以将一些通用的逻辑封装在高阶组件中,然后应用到多个组件上。
  • 状态管理:可以在高阶组件中管理状态,然后将状态和操作传递给子组件。
  • 性能优化:可以在高阶组件中进行性能优化,如缓存组件、避免不必要的渲染等。
function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log(`${WrappedComponent.name} 组件挂载`);
    }

    componentWillUnmount() {
      console.log(`${WrappedComponent.name} 组件卸载`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

function MyComponent() {
  return <div>这是一个组件</div>;
}

const LoggedComponent = withLogging(MyComponent);

function App() {
  return <LoggedComponent />;
}

你可能感兴趣的:(react,react.js,前端,前端框架)