前端八股文react篇

这里是超详细的 React 前端面试"八股文",涵盖了从核心概念到高级技巧的高频考点。

一、核心概念

1. JSX
  • 是什么:JavaScript XML,一种 JavaScript 的语法扩展,允许在 JS 中写类似 HTML 的结构。
  • 本质 :JSX 会被 Babel 编译成 React.createElement() 函数调用,最终返回一个描述 DOM 结构的 JavaScript 对象(即 React元素)。
  • 注意事项
    1. 必须有一个根节点 (或使用 <> 空标签)。
    2. 所有标签必须闭合
    3. 使用 className 代替 classhtmlFor 代替 for
    4. 使用驼峰命名 (如 onClick)。
    5. 可以在 {} 内嵌入 JavaScript 表达式。
2. 组件
  • 函数组件 :现代 React 开发的主流。

    javascript 复制代码
    function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
    }
    // 或使用箭头函数
    const Welcome = (props) => <h1>Hello, {props.name}</h1>;
  • 类组件

    javascript 复制代码
    class Welcome extends React.Component {
      render() {
        return <h1>Hello, {this.props.name}</h1>;
      }
    }

二、数据流与状态管理

1. Props(属性)
  • 特点只读的,从父组件流向子组件。使组件具有可配置性。
  • Children Propprops.children 可以获取到组件开闭标签之间的内容。
  • PropTypes:用于类型检查(现在更推荐 TypeScript)。
2. State(状态)
  • 特点 :组件内部可变的数据,状态改变会触发组件重新渲染。

  • 类组件 State :在构造函数中初始化,使用 this.setState() 更新(是异步的)。

  • 函数组件 State :使用 useState Hook

    javascript 复制代码
    const [count, setCount] = useState(0); // 初始值为 0
    setCount(1); // 直接设置新值
    setCount(prevCount => prevCount + 1); // 使用函数式更新,依赖于前一个状态

Props 与 State 的区别

维度 Props State
来源 父组件传递 组件内部定义
可变性 只读 可变
用途 传递数据和回调函数 管理组件内部状态

三、事件处理与 this 指向(类组件)

在类组件中,事件处理函数的 this 默认是 undefined

解决方法

  1. 在构造函数中绑定 (推荐):

    javascript 复制代码
    constructor(props) {
      super(props);
      this.handleClick = this.handleClick.bind(this);
    }
  2. 使用 Class Fields 语法 (实验性,但常用):

    javascript 复制代码
    handleClick = () => {
      // 这里的 this 指向正确
    };
  3. 在回调中使用箭头函数 (不推荐在 render 中,会导致每次渲染都创建新函数):

    javascript 复制代码
    onClick={() => this.handleClick()}

向事件处理函数传递参数

javascript 复制代码
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

四、组件生命周期(类组件)

挂载

  • constructor()
  • static getDerivedStateFromProps(props, state)(不常用)
  • render()必须纯函数
  • componentDidMount() -> 发起 AJAX 请求、DOM 操作、订阅事件

更新 (由 propsstate 变化引起):

  • static getDerivedStateFromProps(props, state)
  • shouldComponentUpdate(nextProps, nextState) -> 性能优化关键,决定是否重新渲染
  • render()
  • getSnapshotBeforeUpdate(prevProps, prevState)
  • componentDidUpdate(prevProps, prevState, snapshot) -> 可执行 DOM 操作

卸载

  • componentWillUnmount() -> 执行清理工作,如清除定时器、取消网络请求、取消订阅

五、Hooks(函数组件的灵魂)

Hooks 让函数组件拥有状态和生命周期等能力。

1. useState
  • 用于在函数组件中添加状态。
  • 注意 :状态更新函数是异步的,多次 setState 在同一个事件循环中可能会被合并(批处理)。
2. useEffect
  • 用于执行副作用(数据获取、订阅、手动修改 DOM)。
  • 相当于三个生命周期的组合componentDidMount + componentDidUpdate + componentWillUnmount
javascript 复制代码
import { useEffect } from 'react';

// 1. 每次渲染后都执行
useEffect(() => {
  // ... 副作用逻辑
});

// 2. 只在挂载后执行一次 (依赖项为空数组)
useEffect(() => {
  // ... 相当于 componentDidMount
}, []);

// 3. 在挂载后和 count 变化后执行
useEffect(() => {
  // ... 副作用逻辑
}, [count]); // 依赖项数组

// 4. 需要清理的副作用(如订阅、定时器)
useEffect(() => {
  const subscription = props.source.subscribe();
  // 返回一个清理函数,在组件卸载和下一次副作用执行前调用
  return () => {
    subscription.unsubscribe();
  };
}, [props.source]);
3. useContext
  • 用于在组件树中跨层级传递数据,避免 props 逐层传递。
javascript 复制代码
const ThemeContext = createContext('light');
// 顶层组件
<ThemeContext.Provider value="dark">
  <Toolbar />
</ThemeContext.Provider>

// 底层组件
const theme = useContext(ThemeContext);
4. useReducer
  • useState 的替代方案,适用于复杂状态逻辑(多个子值或下一个状态依赖于之前的状态)。
5. useCallbackuseMemo(性能优化)
  • useCallback :缓存函数本身,避免子组件因函数引用不同而无效重渲染。

    javascript 复制代码
    const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
  • useMemo :缓存计算值 ,避免每次渲染都进行高开销计算。

    javascript 复制代码
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
6. useRef
  • 用于获取 DOM 元素或存储一个可变值 ,该值在组件的整个生命周期内保持不变,且更新不会触发重新渲染。

    javascript 复制代码
    const inputEl = useRef(null);
    const onButtonClick = () => { inputEl.current.focus(); };
    return <input ref={inputEl} type="text" />;

六、虚拟 DOM 与 Diff 算法

1. 什么是虚拟 DOM?
  • 一个轻量的 JavaScript 对象,是真实 DOM 的抽象。通过 React.createElement 创建。
2. Diffing 策略

React 的 Diff 算法基于两个假设:

  1. 不同类型的元素会产生不同的树 (如 <div><span>,会直接拆卸重建)。
  2. 通过 key Prop 来标识哪些子元素在不同的渲染中保持稳定。

Diff 过程

  • 树对比:对树进行分层比较,两棵树只会对同一层次的节点进行比较。
  • 组件对比:如果是同一类型的组件,继续对比其子节点/VNode。如果不是,则直接替换整个组件下的所有子节点。
  • 元素对比 :对于同一层级的同类型元素(如 <div>),React 会复用节点,只更新有变化的属性。
  • 列表对比 :对于列表元素,key 至关重要。React 使用 key 来匹配新旧树中的子元素,使元素在重新排序时能够高效地移动,而不是销毁和重建。
3. Key 的作用与为什么不要用 index
  • 作用:帮助 React 识别哪些项被修改、添加或删除,是元素的唯一标识。
  • 为什么不用 index
    • 如果列表顺序变化,index 也会变,导致 key 变化,React 会错误地认为需要重新渲染元素,性能变差
    • 可能导致状态错乱(例如,在受控输入框的列表中,打乱顺序后输入框的内容会和错误的项绑定)。

七、状态提升与组件通信

  1. 状态提升:当多个组件需要反映相同的变化数据时,建议将共享状态提升到最近的共同父组件中去。
  2. 父子通信
    • 父 -> 子:通过 Props
    • 子 -> 父:父组件通过 Props 传递一个回调函数给子组件,子组件调用该函数来向父组件传递信息。
  3. 兄弟通信:状态提升到共同的父组件。
  4. 跨层级/复杂应用通信
    • Context API:适合传递"全局"数据(如主题、用户信息)。
    • 状态管理库Redux (经典,生态成熟),Zustand (轻量简单),MobX(响应式)。

八、受控组件 vs 非受控组件

维度 受控组件 非受控组件
定义 表单数据由 React 组件状态管理 表单数据由 DOM 自身管理
值控制 value={this.state.value} defaultValueref
变化处理 onChange 事件更新 state 通过 ref 从 DOM 节点获取值
推荐度 推荐,数据流清晰 在需要集成非 React 代码或文件上传时使用

示例

javascript 复制代码
// 受控组件
const [inputValue, setInputValue] = useState('');
<input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />

// 非受控组件
const inputRef = useRef();
<input type="text" defaultValue="hello" ref={inputRef} />
// 通过 inputRef.current.value 获取值

九、高阶组件与 Render Props(已逐渐被 Hooks 替代)

  • 高阶组件 :一个函数,接受一个组件,返回一个新的增强组件。用于逻辑复用
  • Render Props:一个值为函数的 Prop,该函数返回一个 React 元素并告知组件要渲染什么。组件通过调用这个函数来渲染内容。

Hooks 已成为逻辑复用的首选方案,因为它们更简洁,避免了 HOC 的嵌套地狱和 Render Props 的回调地狱。


十、常见面试题与手写题

  1. React 事件机制(合成事件)

    • React 使用事件委托 ,将所有事件绑定到 document(v17+ 绑定到 root 容器)上,而不是每个 DOM 节点。
    • 提供与原生事件相同的接口,但跨浏览器兼容。
    • 在事件处理函数执行完后,合成事件对象会被清空,无法被异步访问(如需异步访问,需调用 e.persist())。
  2. setState 是同步还是异步

    • React 事件处理函数和生命周期 中,setState异步的(批处理更新)。
    • setTimeout、原生事件监听器 等异步代码中,setState同步的。
  3. React 性能优化手段

    • React.memo:缓存函数组件,对 Props 进行浅比较,避免不必要的重渲染。
    • useMemo / useCallback:缓存值和函数。
    • shouldComponentUpdate(类组件)或 React.PureComponent
    • 代码分割:React.lazy + Suspense
  4. 什么是 Fiber

    • React 16 引入的新的协调引擎(Reconciliation)。
    • 目的 :将渲染工作拆分成小的单元(Fiber 节点),并允许在浏览器空闲时执行,实现可中断的异步渲染,解决大型应用卡顿问题。
  5. 手写 useState 简易版

    javascript 复制代码
    let state = [];
    let setters = [];
    let stateIndex = 0;
    
    function createSetter(index) {
      return newValue => {
        state[index] = newValue;
        render(); // 触发重新渲染
      };
    }
    
    function useState(initialValue) {
      state[stateIndex] = state[stateIndex] ?? initialValue;
      setters[stateIndex] = setters[stateIndex] ?? createSetter(stateIndex);
      const value = state[stateIndex];
      const setValue = setters[stateIndex];
      stateIndex++;
      return [value, setValue];
    }
    
    // 每次渲染后重置索引
    function render() {
      stateIndex = 0;
      // ... React 的渲染逻辑
    }

这份指南涵盖了 React 面试的核心知识体系。理解这些概念并能结合代码示例阐述,你将能应对绝大多数 React 相关的面试问题。

相关推荐
打小就很皮...3 小时前
React 实现 i18next 中英文切换集成
前端·react.js·i18next
拉不动的猪3 小时前
函数组件和异步组件
前端·javascript·面试
淮北4943 小时前
html + css +js
开发语言·前端·javascript·css·html
你的人类朋友3 小时前
适配器模式:适配就完事了bro!
前端·后端·设计模式
Setsuna_F_Seiei4 小时前
CocosCreator 游戏开发 - 利用 AssetsBundle 技术对小游戏包体积进行优化
前端·cocos creator·游戏开发
黄毛火烧雪下4 小时前
高效的项目构建和优化之前端构建工具
前端
90后的晨仔4 小时前
在 macOS 上轻松获取 GIF 图片总时长:多种实用方法详解
前端
技术钱4 小时前
vue3前端解析excel文件
前端·vue.js·excel
mapbar_front5 小时前
顺利通过试用期:避开三大陷阱,掌握三个关键点
前端·面试