📌 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 的对比

特性ReactVue
数据绑定单向数据流双向数据绑定(v-model)
编程范式函数式(Hooks)、JSX模板驱动、选项式(Vue 3 支持组合式)
虚拟 DOM
状态管理Redux、Context、Zustand 等Vuex、Pinia
生态工具Next.js、Umi、RemixNuxt.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
ProfilerReact 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 />;
}

📌 21. React 中的生命周期

一、React 生命周期阶段划分

1. 挂载阶段(Mounting)
组件首次被创建并插入 DOM 的过程:

  • constructor()
    初始化 state 和绑定事件处理函数,避免在此进行副作用操作(如 API 请求)。
    static getDerivedStateFromProps()
    静态方法,用于根据 props 更新 state,触发于 render 之前。不常用,可能导致代码复杂。
  • render()
    返回 JSX 结构,纯函数,不修改 state。
  • componentDidMount()
    组件已被渲染到 DOM 后执行,适合初始化操作(如 API 请求、订阅事件)。

2. 更新阶段(Updating)
props 或 state 变化触发组件重新渲染:

  • static getDerivedStateFromProps()
    同上,更新阶段也会触发。
    shouldComponentUpdate(nextProps, nextState)
    控制组件是否需要重新渲染,默认返回true。可通过浅比较(React.memo)优化性能。
  • render()
    重新计算并返回新的 JSX。
  • getSnapshotBeforeUpdate(prevProps, prevState)
    在 DOM 更新前获取当前 DOM 状态(如滚动位置),返回值将作为componentDidUpdate的第三个参数。
  • componentDidUpdate(prevProps, prevState, snapshot)
    DOM 已更新后执行,可在此操作 DOM 或根据新旧 props/state 执行副作用。

3. 卸载阶段(Unmounting)
组件从 DOM 中移除时:

  • componentWillUnmount()
    清理工作(如取消订阅、清除定时器),避免内存泄漏。

二、生命周期函数详解与应用场景
1. 挂载阶段核心函数

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this); // 绑定this
  }

  componentDidMount() {
    // 初始化操作:数据获取、订阅事件
    fetchData().then(data => this.setState({ data }));
    window.addEventListener('resize', this.handleResize);
  }

  render() {
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

2. 更新阶段性能优化

class ListItem extends React.Component {
  // 浅比较props,避免不必要的渲染
  shouldComponentUpdate(nextProps) {
    return this.props.item.id !== nextProps.item.id;
  }

  render() {
    return <div>{this.props.item.name}</div>;
  }
}

3. 卸载阶段资源清理

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  componentDidMount() {
    this.timerID = setInterval(() => {
      this.setState(prevState => ({ seconds: prevState.seconds + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID); // 清除定时器,防止内存泄漏
  }

  render() {
    return <div>Seconds: {this.state.seconds}</div>;
  }
}

三、React 16.8+ Hooks 生命周期替代方案

1. useEffect () 替代生命周期函数

import React, { useState, useEffect } from'react';

function Example() {
  const [count, setCount] = useState(0);

  // 相当于componentDidMount + componentDidUpdate
  useEffect(() => {
    document.title = `You clicked ${count} times`; // 副作用操作

    // 相当于componentWillUnmount(返回清理函数)
    return () => {
      // 清理工作,如取消订阅
    };
  }, [count]); // 仅在count变化时执行

  return <button onClick={() => setCount(count + 1)}>Click me</button>;
}

2. useLayoutEffect () 与 useEffect () 的区别

  • useEffect():异步执行,DOM 更新后触发(适合大多数副作用)。
  • useLayoutEffect():同步执行,DOM 更新前触发(适合需要测量 DOM 的场景)。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐