React知识点 60问 2025

答案见最下方:

一、请简单介绍一下React18有哪些更新。

二、JSX是什么,它和JS有什么区别?

三、为什么在文件中没有使用React ,也要在文件顶部import React from “react”

四、请说一下React 事件机制和JS原生事件机制的区别?

五、React 事件与原生事件的执行顺序?

六、请说说React Hooks解决了什么问题? 函数组件与类组件的区别

七、为什么 useState 要使用数组而不是对象

八、说一下常用的Hook

九、Hook 的使用限制有哪些?

十、useEffect 与 useLayoutEffect 的区别

十一、如何自定义Hook

十二、React 高阶组件是什么,和普通组件有什么区别,适用什么场景

十三、React 高阶组件、Render.props、Hooks 有什么区别,为什么要不断迭代

十四、React组件通信有哪些方式

十五、React中props和state有什么区别?React中的props为什么是只读的?

十六、React 16.X 中 props 改变后在哪个生命周期中处理

十七、React 性能优化在哪个生命周期?它优化的原理是什么

十八、React 中 keys 的作用是什么?

十九、React 中 refs 的作用是什么?

二十、说说React diff 算法

二十一、React 与 Vue 的 diff 算法有何不同?

二十二、React setState 调用的原理

二十三、setState 第一个参数有两种传递方式 1.一个对象 2. 一个函数 这两种写法有什么区别呢?useState呢

二十四、React Hooks 工作机制深度解析

二十五、React中setState的第二个参数作用是什么?

二十六、React中的setState和replaceState的区别是什么?

二十七、React中的setState批量更新的过程是什么?

二十八、React 中 setState 什么时候是同步的,什么时候是异步的?

二十九、React Hooks 设计动机与设计模式解析

三十、React实现缓存的方式有哪些?他们有什么区别?

三十一、对React中Fragment的理解,它的使用场景是什么?

三十二、什么是受控组件和非控组件

三十三、React.Component 和 React.PureComponent 的区别

三十四、对有状态组件和无状态组件的理解及使用场景

三十五、useffect 模拟生命周期

三十六、哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?

三十七、hooks父组件怎么调用子组件的方法?

三十八、React的严格模式如何使用,有什么用处?

三十九、React的设计思想是什么

四十、为什么React自定义组件首字母要大写

四十一、React组件为什么不能返回多个元素

四十二、简述React的生命周期

四十三、对React的插槽(Portals)的理解,如何使用,有哪些使用场景

四十四、父组件和子组件的生命周期执行顺序

四十五、React声明组件有哪几种方法,有什么不同?

四十六、React组件的构造函数有什么作用?它是必须的吗?

四十七、React性能优化手段

四十八、在React中如何避免不必要的render?

四十九、React-Router工作原理

五十、对 Redux 的理解,主要解决什么问题

五十一、Redux 原理及工作流程

五十二、Redux 中异步的请求怎么处理

五十三、Fiber架构

五十四、React错误处理?

五十五、数据双向绑定的原理

五十六、怎么动态导入组件,按需加载,代码分割?

五十七、React的状态提升是什么?使用场景有哪些?

五十八、对 React 和 Vue 的理解,它们的异同

五十九、对React SSR的理解

六十、react-redux中的connect怎么实现?

六十一、diff的时间复杂度是多少,为什么?



一、请简单介绍一下React18有哪些更新。
1. 并发渲染
并发渲染实际上不是一个功能,而是React的新的底层渲染机制,使得React可以同时准备多套UI。
并发模式的一个最重要的特性渲染可中断。React18之前的页面渲染是一个一个同步事务进行处理,是一旦开始就无法中断。
并发模式意味着,所有的UI显示任务都是可以被中断的,它可以先执行某个渲染更新然后挂起等待。这意味着渲染必须在DOM树计算完毕以后再执行。React就可以在这期间准备新的更新,而不影响操作的执行。
另一个特性则是可重用性,React可以在渲染某个更新时移除一部分UI,然后在稍后的更新中把它再加回来。
2. 过渡更新
通常为了区分紧急更新和非紧急更新,一般来说紧急更新主要是比如输入、按压、点击等需要马上表现出来的更新,过渡更新则是用于查询结果之类的不那么需要紧急表现得更新操作,通常情况下一般的更新操作都是紧急的,如果需要开启过渡更新,则需要调用例如useStransitionstartStransition,紧急更新会中断过渡更新,多个过渡更新被中断,react会抛弃之前的更新内容,仅仅渲染最新的内容。
3. 自动批处理
React18之前,react不会对promisesetTimeout、事件处理之内的操作进行批处理,只会对promisesetTimeout、事件处理以外的操作进行批处理,react18则优化了这一部分,使得在React的任何地方都可以进行自动批处理,当然你也可以使用flushSync退出批处理.

flushSync(()=>setState(val=>val+1))

对于class有影响,因为在class中可以在18之前在setTimeoutpromise中是不会自动批处理的,则在setTimeoutsetState之后是可以拿到this.state最新的值的,但是在react18因为是自动批处理则现在是拿不到this.state的最新的值的,如果需要获取的话则需要用flushSync.

  setTimeout(()=>{
        this.setState({count}=>count+1);
        console.log(this.state.count) //1
        this.setState({age}=>{age:20})
  },100) //17
  setTimeout(()=>{
        this.setState({count}=>count+1);
        console.log(this.state.count) //0
        this.setState({age}=>{age:20})
  },100) //18
  setTimeout(()=>{
        flushSync(()=> {
          this.setState({count}=>count+1)}
        }
        console.log(this.state.count) //1
        this.setState({age}=>{age:20})
  },100) //18

4. useId Hook
用于客户端和服务端渲染生成唯一的Id。
5. startTransition Api / useStransitionHook
开启过渡渲染
6. useDeferredValue Hook
用于延迟更新某些UI部分。比如结合Suspense做异步加载.
你可以将useDeferredValue作为性能优化的手段,当你的某个UI渲染很慢,你又希望其避免阻塞其他渲染,则你就可以用useDeferredVaue
7. Suspense 支持SSR
底层实现依赖错误边界组件,当Suspense捕获到子组件的promise,会优先展示fallback的ui,直到返回resolved状态再重新渲染。

二、JSX是什么,它和JS有什么区别?
1、语法:JSX的语法类似于HTML; JS是一门编程语言。
2、功能:JSX是定义React组件的UI;JS是用于逻辑的控制和操作。
3、处理:JSX会被转换为JS,不可直接执行;JS是可直接执行;
4、表达式:JSX中{}中可以嵌入JS表达式;
5、不用JSX怎么生成React组件呢?

  React.createElement('div','组件')

三、为什么在文件中没有使用React ,也要在文件顶部import React from “react”
因为使用了JSX;

四、请说一下React 事件机制和JS原生事件机制的区别?
1、绑定方式不一样:一个是绑定到JSX上面,一个是绑定到HTML上、动态绑定或者adeventListener;
2、事件对象不一样:React做了兼容处理,可以兼容所有的浏览器;原生需要自己做兼容处理;
3、事件传播不一样:React事件默认只支持冒泡,捕获阶段通过 onClickCapture 监听;JS原生支持冒泡和捕获;
4、事件处理不一样:React是把所以事件集中在一起,更加节约性能;JS原生是每个事件绑定到对应的节点上面;
5、解绑不一样:React不需要解绑操作;JS原生需要解绑操作;

五、React 事件与原生事件的执行顺序?
原生事件优于React事件,首先执行捕获阶段、然后是冒泡阶段,先执行原生的捕获,然后是React的捕获,然后是原生的冒泡,再是React的冒泡。无论是原生还是React,捕获都是优于冒泡;React的事件是委托到根节点的而不是节点本身。

六、请说说React Hooks解决了什么问题? 函数组件与类组件的区别
1、复杂性 当class类组件过于庞大难于理解,Hooks提供了更加简洁易于理解的代码来管理状态和生命周期;
2、逻辑复用,方便你在任何地方使用;
3、冗余代码,可以减少大量冗余代码;
函数组件和类组件的区别:
1、定义方式:使用js函数来定义;使用ES6的class类来定义
2、状态管理:使用useState管理;使用this.statesetState管理
3、生命周期:使用useEffect管理;使用componeDidMount等;
4、性能优化:使用useMemo等;使用React.memo,shouldComponentUpdate
5、代码复用:逻辑交互可以在任何地方复用;代码复用性差;

七、为什么 useState 要使用数组而不是对象
主要原因是可以自定义命名;

八、说一下常用的Hook
1、useState
2、useEffect
3、useMemo
4、useCallback
5、useRef
6、useReducer
7、useLayoutEffect
8、useContext

九、Hook 的使用限制有哪些?
只能在函数组件的顶部使用;不可在for循环、条件语句中使用;不可在JS文件、class组件中使用;

十、useEffect 与 useLayoutEffect 的区别
1、执行时机不同:useEffect是在页面加载和渲染完毕以后异步执行;useLayoutEffect是在页面更新和渲染之前执行,是同步执行
2、性能不同:useEffect因为是异步的,是不会阻塞页面加载;useLayoutEffect可能会导致页面阻塞
3、使用场景:一般用于ajax请求、事件监听等不影响页面布局的操作;一般用于需要获取DOM节点的大小位置等操作;

十一、如何自定义Hook

  const useFetch = (url)=>{
      const [loading,setLoading] = useState(false);
      const [error,setError] = useState('');
      const [result,setResult] = useState(null);
      useEffect(()=>{
        try{
            setLoading(true)
            axios.get(url).then((res)=>{
                setResult(res);
                setLoading(false)
            }).catch((err)=>{
                setError(err);
                setLoading(false)
            })
        catch(error){
            setError(error);
            setLoading(false)
        }
      },[url])
      return {loading,error,result}
  }

  const [loading,error,result] = useFetch('http:172.27.1.1/api/test/')

十二、React 高阶组件是什么,和普通组件有什么区别,适用什么场景
是一种高级的组件设计模式,而不是ReactApi的一部分,接受一个组件然后返回一个新的组件,缺点是会污染传入组件的props,难以访问原组件的状态和生命周期
特点:
1、高复用性:HOC使得组件之间的逻辑代码可以复用
2、状态抽象:HOC可以封装和管理组件状态
3、增强功能:HOC可以轻松的给组件增强功能;
适用于:
权限管理、数据处理、状态管理

十三、React 高阶组件、Render.props、Hooks 有什么区别,为什么要不断迭代
Render.props是一种把React作为元素返回的组件,会导致嵌套地狱;

class MouseTracker extends React.Component {
  constructor(props) {
    super(props);
    this.state = { x: 0, y: 0 };
  }
 
  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };
 
  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

<MouseTracker render={({ x, y }) => (
  <MouseTracker render={({ x, y }) => (
    <h1 style={{ color: 'red' }}>鼠标位置: ({x}, {y})</h1>
  )}/>
)}/>

Hooks是在函数组件中使用,并且解决了Hoc和Render.props的缺点。

十四、React组件通信有哪些方式
1、父传子
props
2、子传父
props传函数进来,refs获取子组件实例

// 父组件
function Parent() {
  const handleChildEvent = (data) => {
    console.log("Received data from child:", data);
  };
  return <Child onEvent={handleChildEvent} />;
}

// 子组件
function Child({ onEvent }) {
  return <button onClick={() => onEvent("Child Data")}>Send Data</button>;
}

// 父组件
function Parent() {
  const childRef = useRef(null);
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.current.focusInput()}>
        聚焦子组件输入框
      </button>
    </div>
  );
}

// 子组件(需使用 forwardRef 转发 ref)
const Child = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focusInput: () => inputRef.current.focus(),
  }));
  return <input ref={inputRef} />;
});

3、跨层级
ContextApi

//context.js
import React, { createContext, useContext, useState } from 'react';
// 创建Context
export default const MessageContext = createContext();

// 父组件
import MessageContext from './context.js'
const ParentComponent = () => {
    const [message, setMessage] = useState("Hello from Context!");

    return (
        <MessageContext.Provider value={{ message, setMessage }}>
            <ChildOne />
            <ChildTwo />
        </MessageContext.Provider>
    );
};

// 子组件1
import MessageContext from './context.js'
const ChildOne = () => {
    const { setMessage } = useContext(MessageContext);

    const updateMessage = () => {
        setMessage("Updated message from Child One!");
    }

    return (
        <div>
            <button onClick={updateMessage}>Update Message</button>
        </div>
    );
};

// 子组件2
import MessageContext from './context.js'
const ChildTwo = () => {
    const { message } = useContext(MessageContext);

    return (
        <div>
            <h2>Message: {message}</h2>
        </div>
    );
};

export default ParentComponent;

4、兄弟层级
通过公用的父组件来传递参数
5、全局状态
ReduxZustand

十五、React中propsstate有什么区别?React中的props为什么是只读的?
props是只读的不可编辑,state可以编辑
props是父组件传入的属性数据,state是组件内部的属性数据,在constructor中初始化;
props在组件初始化后就不可变,state是可以在组件内不同生命周期改变

保证React单向数据流的设计模式,父组件可以向不同的组件传递props。

十六、React 16.X 中 props 改变后在哪个生命周期中处理
1、componentReceviedProps()(16.3以后已经废弃);
2、componentDidUpdate()
3、getDerivedStateFromProps()

十七、React 性能优化在哪个生命周期?它优化的原理是什么
shouldComponentUpdate ;此外在16.3以后还加入了React.PureComponentReact.memo
shouldComponentUpdate 可以对stateprops的更新做处理,如果不需要则返回false
React.PureComponent的功能是对stateprops做浅比较,如果浅比较都不需要更新,那么就不更新
React.memoReact.PureComponent的功能类似,只是是用于函数组件
优化的原理:
主要原理就是就是为了减少渲染以及减少diff算法,因为diff算法是非常宝贵的,减少diff算法和算法就可以优化极大的性能。

十八、React 中 keys 的作用是什么?
帮助React高效的识别和精准的跟踪DOM中的变化,比如删除、添加、修改等操作,特别是动态列表和动态排序的时候,帮助React判断哪些节点需要移除或者重新排序,减少DOM操作。

十九、React 中 refs 的作用是什么?
用于直接访问DOM元素或者组件实例。

二十、说说React diff 算法
React的diff算法是基于索引的比较,是新旧虚拟DOM树之间进行比较,尽可能的减少真实的DOM操作,提高性能,React会先比较组件的类型和属性,只有在必要的时候才更新其内部元素;
Diff算法的关键策略:
1、分层比较:将整个UI拆分成树状结构,比较每一个层级,只会比较不同类型的组件,如果是不同类型的组件会直接删除替换;
2、统计比较:统计层级的节点会根据key值进行比较,key帮助react精确识别每个元素的位置,优化重排;
3、元素更新与删除:如果两个节点具有相同的key,react会更新他们,否则会删除并创建新的节点;
4、避免不必要的更新:如果state和props没有变化,则不会重新渲染组件。
具体步骤:
1、先比较类型:如果类型不同则直接销毁旧节点,创建新节点。
2、同类型比较:如果类型是相同的,那么react会根据其属性、子节点,比较他们的差异并更新;
3、递归对比子节点:React会递归的对比所有的子节点,重复以上的步骤,并且尽量重用已有的key;
key的作用:
用于标识哪个节点发生了变化,帮助React判断哪些节点需要移除或者重新排序,减少DOM操作;

二十一、React 与 Vue 的 diff 算法有何不同?
React的diff算法是基于索引的比较,主要是对新旧虚拟DOM树进行比较,尽可能的减少DOM操作,提高性能,React会优先比较组建的类型和属性,只有在必要的时候才会更新其内部元素;
Vue的diff算法是基于节点的比较,而且是采用双端的算法,从前后两端先进行比较,然后再向中间进行比较;Vue对于Key的使用较少,在没有Key的时候,Vue会默认按照位置进行节点重排,所以会有些性能问题。

二十二、React setState 调用的原理
1、触发更新
当调用setState时React会将这个状态更新表记为待处理。
2、合并状态
在ReactsetState是异步的,所以当有多个setState操作时,这些调用会被合并成为一个。
3、调度更新
React使用了调度来更新这些状态,在16和之后的版本中因为引入了Fiber架构。当调用setState的时候,React会将该更新加入到Fiber的更新队列中。
4、执行渲染
在浏览器的下一个事件循环中,React会从Fiber的更新队列中取出需要更新的组件,并根据新的状态和属性进行渲染逻辑。这一步会调用组件的render方法,同时会比较新旧的虚拟DOM的差异,最终只对差异的部分进行实际的DOM更新。

二十三、setState 第一个参数有两种传递方式 1.一个对象 2. 一个函数 这两种写法有什么区别呢?useState呢

// 连续调用两次,结果可能只加 1(而非预期的加 2)
handleClick = () => {
  this.setState({ count: this.state.count + 1 });
  this.setState({ count: this.state.count + 1 });
};
// 连续调用两次,结果会正确加 2
handleClick = () => {
  this.setState((state) => ({ count: state.count + 1 }));
  this.setState((state) => ({ count: state.count + 1 }));
};

useState道理也是一样的
二十四、React Hooks 工作机制深度解析
1、HooksFiber架构的绑定机制;
状态存储位置:
每个Hook创建时,会在组件的Fiber节点的memoizedState属性中以链表行书存储。
每个Hook节点包含memoizedState(上一次渲染的状态值)、quene(更新队列)和next(指向下一个Hook)字段。
执行顺序的强制性:
Hooks必须保证每次渲染的调用顺序一致,否则会因链表节点错位导致状态错乱给,所以不能再iffor循环中使用Hook

if (condition) {
  const [count] = useState(0);  //  破坏 Hook 链表顺序
}

2、核心Hooks实现原理
useState状态管理机制
首次渲染:初始化Hook节点,将初始值存入memoizedState
更新阶段:通过dispatch函数将更新操作加入队列,触发重新渲染并计算新状态。
闭包陷阱:异步回调如定时器、事件监听,直接使用state可能引用旧值,可能导致更新丢失(需通过函数式更新解决或者用useRef同步最新的值)

setCount(prev => prev + 1);  // ✅ 正确方式  
setCount(count + 1);         // ❌ 可能依赖过期闭包值  

useEffect副作用调度
副作用函数在提交阶段异步执行,避免阻塞渲染流程
依赖数组对比,空数组只执行一次
清理机制:返回的清理函数会在组件卸载或依赖变化前执行。

3、Hooks渲染流程
挂载阶段:
创建Fiber节点并创建Hook链表;
执行组件函数,触发Hooks初始化逻辑;
更新阶段:
遍历Hook链表,依次应用更新队列中的操作;
生成新虚拟DOM,通过Diff算法更新视图;
卸载阶段:
执行useEffect返回的清理函数;
移除Fiber节点及关联的Hook链表

二十五、React中setState的第二个参数作用是什么?
回调函数,可以用来做执行完setState后需要的操作,比如打印日志、获取最新的状态之类的。

二十六、React中的setState和replaceState的区别是什么?
setState只是做浅合并,不会影响到其他属性,提高性能。
replaceState是完全覆盖,可能会删除某些属性(不推荐使用)。

二十七、React中的setState批量更新的过程是什么?
调用setState,组件的state不会立即更新,setState只是把需要修改的state放到一个队列里面,并且是只要还有同步任务,就会一直把需要更新的state放到队列里面去,同时会把多次setState的状态最终合并到一起,这就是批量更新。

二十八、React 中 setState 什么时候是同步的,什么时候是异步的?
react18之前在setTimeOutpromisesetInterval、原生adeventListener中是同步的
合成事件中、生命周期中是异步的。
在React18之后都是异步的

二十九、React Hooks 设计动机与设计模式解析
1、设计动机
逻辑复用困境:
类组件通过HOC或者Render props实现逻辑复用,导致组件嵌套层级过深,代码耦合度增加
共享状态逻辑需要依赖复杂的设计模式,难以跨组件高效复用
生命周期分散性
关联逻辑被拆分到不同的生命周期比如componentDidMountcomponentDidUpdate,代码冗余易遗漏清洗操作。
类组件中的this绑定问题与实例方法管理复杂度高

函数组件的增强需求
函数组件无法管理状态和副作用,导致其能力不足,所以用useStateuseEffect来补齐短板

2、设计模式
状态和UI分离模式,拆分组件样式和组件逻辑代码成为自定义的hooks,减少代码耦合。
副作用统一管理
useEffect整合类组件的componentDidMountcomponentDidUpdatecomponentWillUnmount生命周期,通过依赖数组控制执行时机;
清理机制:返回清理函数避免内存泄漏(如取消网络请求、移除事件监听)
性能优化模式
缓存策略:useMemo缓存计算结果,useCallback缓存函数引用,减少不必要的渲染
惰性初始化:传递函数给useState初始化复杂状态,避免重复计算

三十、React实现缓存的方式有哪些?他们有什么区别?
React QuerySWR
localStoragesessionStorage
React.memoReact.PureComponent
useMemouseCallback
持久化:只有localStoragesessionStorage是长期持久化的,sessionStorage在关闭窗口后也会消失,但是刷新不会消失,其他的都会消失。
使用场景:
localStoragesessionStorage存放需要持久化存放的数据
React.memoReact.PureComponent主要用于class组件的缓存或者复杂计算结果的缓存。
React QuerySWR主要用于异步存储数据和缓存。
useMemouseCallback主要是hooks中使用对函数组件的缓存

三十一、对React中Fragment的理解,它的使用场景是什么?
Fragment组件实际上是因为React不能直接返回多个节点,如果需要返回多个节点又不想同时新增其他实际的节点去包裹他们,那么就可以用Fragment节点包裹需要返回的子节点,它还可以写为<></>;

三十二、什么是受控组件和非控组件
使用React来管理表单状态数据的组件称为受控组件
不需要React来管理表单状态数据的组件成为非受控组件,用ref来获取表单的内容

三十三、React.Component 和 React.PureComponent 的区别
PureComponent组件会对propsstate进行浅比较,避免不必要的更新,实际上是实现了shouldComponetUpdate的功能;

三十四、对有状态组件和无状态组件的理解及使用场景
有状态组件:
有生命周期、class组件、有this、有继承、内部使用state管理状态
无状态组件:
可以是calss组件也可以是函数组件、无this、不使用生命周期、无state管理,完全依赖外部的props做展示作用

三十五、useffect 模拟生命周期
componentDidMount:

useEffect(()=>{
//加载
},[])

componentWillUnmount:

useEffect(()=>{
   return ()=>{
    //卸载 
    }
})

componentDidUpdate:

useEffect(()=>{
  //没有任何等于监听所有的更新
})

三十六、哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?
1.setState()方法被调用
2.父组件重新渲染
3.props更新
4.ContextApi
5.全局状态管理
重新渲染render首先会比较新旧DOM树,根据比较出来的结果计算出需要更新的差异,然后动态更新需要更新的部分。

三十七、hooks父组件怎么调用子组件的方法?
首先需要forwardRef包裹子组件

const child = forwardRef((ref)=>{
  //然后需要使用useImperativeHandle暴露对应的方法给父组件
    useImperativeHandle(ref,()=>({
          someMethod:()=>{
          }  
    })
})
const parent = ()=>{
const ref = useRef(null)
  const childMethd = ()=>{
    if(ref.current){
      ref.current.someMethod();
    }
  }
   return (
      ...
      <Child ref={ref} />
    )
 }

三十八、React的严格模式如何使用,有什么用处?
通过React.strictMode启用,帮助检验不规范的代码,提升代码质量,只在开发环境中生效。

三十九、React的设计思想是什么
组件化:把用户界面分解成可复用的组件
虚拟DOM:避免直接的DOM操作,虚拟DOM是真实DOM的映射关系,使用虚拟DOM优化更新渲染的性能,避免大量且频繁的DOM操作
数据驱动视图:数据更新就重新渲染页面,不需要额外的DOM操作

四十、为什么React自定义组件首字母要大写
因为如果是小写的会被当做HTML来处理,所以需要首字母大写。

四十一、React组件为什么不能返回多个元素
因为这是为了虚拟DOM,虚拟DOM只有一个根节点时,才能够更加高效容易的计算出差异并进行最小化的DOM更新。

四十二、简述React的生命周期
分为三个阶段:
组件装载阶段:挂载阶段组件会被创建,然后被插入到DOM中,完成组件的渲染,该过程只会发生一次;
该过程中会执行constructorgetDerivedStateFormPropsrendercomponentDidMount;
组件更新阶段:当组件的propsstate等触发更新时,会重新渲染,这个过程可能会执行很多次
该过程中会执行:
getDerivedStateFromPropsshouldComponentUpdaterendergetSnapShotBeforeUpdatecomponentDidUpdate
getSnapShotBeforeUpdate:这个是在render之后执行,在componentDidUpdate之前调用必须和componentDidUpdate一起使用,有两个参数一个是prePorpspropsState表示更新之前的stateprops;典型场景包括:
记录滚动位置,以便更新后恢复
获取元素尺寸或位置等瞬时状态
与 componentDidUpdate 配合使用
该方法返回的值会作为第三个参数传递给 componentDidUpdate,形成数据传递链路:

getSnapshotBeforeUpdate(prevProps, prevState) {
  return { scrollTop: document.getElementById('list').scrollTop };
}
componentDidUpdate(prevProps, prevState, snapshot) {
  if (snapshot) {
    // 根据快照恢复滚动位置
  }
}

组件卸载阶段:
只有一个componentWillUnmount会卸载和销毁组件的所有内容,比如定时器和网络请求等。

四十三、对React的插槽(Portals)的理解,如何使用,有哪些使用场景
插槽指的是可以将子组件渲染到除父组件之外的DOM节点中,主要用于模态框、提示框等。

const Modal = ()=>{
  return ReactDOM.createPortal(
    <div>提示框</div>,
    document.getElementById('root')
  )
}

四十四、父组件和子组件的生命周期执行顺序
conctructor
conctructor
render
render
componentDidMount
componentDidMount

四十五、React声明组件有哪几种方法,有什么不同?
1.React.createClassReact.Component、函数组件
React.createClassReact.Component
React.createClass:是ES5的写法现在已经不推荐了
React.Component:是ES6的写法他和函数组件的区别主要是,拥有生命周期,拥有this,拥有构造函数,可以继承,不支持hooks,拥有状态管理。

四十六、React组件的构造函数有什么作用?它是必须的吗?
是用来初始化组件和绑定事件处理的方法,不是必须的。

四十七、React性能优化手段
缓存比如React.memouseMemouseCallback等来做缓存
避免不必要的重新渲染,可以使用React.fragmentshouldComponentUpdateReact.PureComponent
列表加key:渲染列表提供一个唯一的key,帮助React在Diff算法的时候更加精准高效的找到对应的差异,并精确更新。
懒加载和代码分割:使用懒加载来加载页面,可以减少加载的时间,提高性能。
计算密集型任务:可以使用Web Workers,可以避免阻塞UI页面。
服务端渲染SSR

四十八、在React中如何避免不必要的render?
缓存比如React.memouseMemouseCallback等来做缓存
避免不必要的重新渲染,可以使用React.fragmentshouldComponentUpdateReact.PureComponent

四十九、React-Router工作原理
原理:监听URL变化,动态渲染组件,从而实现页面的跳转和切换。
两种路由模式:
hash:使用window.location.hash 监听hash的变化 路径中带有#;通过window.onhashchange监听
history:使用window.history.pushStatewindow.history.replaceState修改URl的变化,路径中没有#;通过window.onpopstate监听;
使用LinkuseNavigate更改路由地址;

五十、对 Redux 的理解,主要解决什么问题
是一个用于javascript的应用的状态管理库,主要是解决了复杂单页面应用开发中状态管理非常困难的问题,Redux通过提供一个集中式的状态管理,将整个应用的状态保存在一个地方,便于管理和维护。
Redux的核心概念和设计理念
1、单一数据源:所有数据都存储在单一的store
2、状态是只读的:唯一可以改变状态的方法是通过发送一个action,意味着每次操作都是可记录和追踪的
3、使用纯函数来执行状态的变化:通过reducers纯函数来描述如何根据action更新状态,确保状态更新的可预测性。

主要解决的问题:
集中式管理状态:集中的管理所有状态,便于管理与维护
状态可预测性:状态更改需要action操作,就可以追溯所有的操作
方便的状态共享:所有组件都可以访问Redux里面的状态
时间旅行调试:Redux结合了不可变的数据和纯函数的概念,使得所有的操作都是可记录。
中间件的支持: 可以方便的扩展功能

五十一、Redux 原理及工作流程
Redux的工作流程:
1、派发Action:用户操作会派发一个action,这个action是一个描述事件的对象
2、处理Action: Reducers是一个纯函数没接收当前的stateaction,返回新的state
3、更新Store:store接受到新的state,更新其内部状态,所有组件都可以从store获取到最新的状态。

一、三大核心要素
Store(单一数据源)

全局唯一的状态容器,存储整个应用的所有共享状态
通过 store.getState() 获取当前状态,通过 store.dispatch(action) 触发状态更新
示例结构:

{
  user: { name: 'Alice' },
  todos: ['Task 1', 'Task 2']
}

Action(状态变化的描述)

一个普通 JavaScript 对象,必须包含 type 字段描述操作类型
可选 payload 字段携带数据,例如:

{ 
  type: 'ADD_TODO', 
  payload: 'Learn Redux' 
}

Reducer(状态变化的纯函数)

接收当前 state 和 action,返回新状态(不可直接修改原状态)
示例:

const todoReducer = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO': 
      return [...state, action.payload];
    default: 
      return state;
  }
};

二、核心原则
单一数据源
整个应用状态仅存储在一个 Store 中,便于调试和管理
状态只读
唯一修改状态的方式是派发 Action,禁止直接修改 Store 数据
纯函数修改
Reducer 必须是纯函数(相同输入必定得到相同输出,无副作用)

五十二、Redux 中异步的请求怎么处理
可以使用中间件,比如redux-thunkredux-saga

五十三、Fiber架构
Fiber是一种基于链表的数据结构,他将渲染过程拆分成多个小的任务单元。这种机制允许React在执行这些任务单元时,根据浏览器空闲的时间暂停、恢复和重新安排任务的执行顺序。这样当浏览器有需要紧急处理的交互或者更新时,React可以暂停当前的渲染任务,将控制权交给浏览器,等浏览器空闲的时候再继续执行渲染任务,从而避免长时间占用主线程,提高页面的响应性。
Fiber解决的问题:
1、可中断和恢复的渲染:Fiber允许渲染过程在需要时暂停和恢复,避免长时间占用主线程,提高页面的响应性。
2、优先级调度:Fiber引入了优先级调度的概念,不同类型的更新可以被赋予不同的优先级。高优先级的更新如用户的交互,会被优先处理,确保用户交互能够及时响应,同时合理安排低优先级的任务的执行。
3、更细粒度的渲染控制:通过Fiber,react可以对渲染过程进行更细粒度的控制,开发者可以根据具体需求设置不同的有优先级和调度策略。
Fiber的优势:
可中断和恢复的机制:根据浏览器的空闲情况,可以暂停和恢复任务。
增量渲染:拆分任务为多个任务,根据任务的优先级等进行调度。
更好的用户体验:优先处理用户的交互事件

五十四、React错误处理?
try-catch
ErrorBoundary

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  componentDidCatch(error, info) {
    this.setState({ hasError: true });
    logErrorToService(error, info.componentStack);
  }
  
  render() {
    return this.state.hasError 
      ? <FallbackUI /> 
      : this.props.children;
  }
}

<ErrorBoundary>
  <UnstableComponent />
</ErrorBoundary>

Susponse

五十五、数据双向绑定的原理
1、React是通过propsstate来实现状态和单项数据流的,当propsstate改变都会触发页面的重新渲染,
2、受控的组件是根据state和表单中的valueonChange事件来实现数据双向绑定的效果。

五十六、怎么动态导入组件,按需加载,代码分割?
React.lazy动态加载组件,并且配合Suspense来动态展示组件的内容。
也可以在React router中使用React.lazySuspense来处理。
代码分割则可以使用Webpack来把应用分解为更小、更易管理的块。
一、动态导入基础方案
使用 React.lazy + Suspense
适用于组件级懒加载,需配合 fallback 占位

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

动态导入函数模块
通过 import() 语法实现非组件代码分割

import('./math').then(math => {
  console.log(math.add(2, 3));
});

二、路由级按需加载
React Router 集成
结合路由配置实现页面级懒加载

const Home = React.lazy(() => import('./routes/Home'));
const About = React.lazy(() => import('./routes/About'));

function RouterConfig() {
  return (
    <Routes>
      <Route path="/" element={<Suspense fallback={...}><Home /></Suspense>} />
      <Route path="/about" element={<Suspense fallback={...}><About /></Suspense>} />
    </Routes>
  );
}

代码分割:
SplitChunks 配置
分离第三方库和公共代码

// webpack.config.js
optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        priority: 10
      }
    }
  }
}

五十七、React的状态提升是什么?使用场景有哪些?
就是通过在父组件中保存状态和修改状态的方法,通过props把状态值和更改状态的方法一起传给子组件,子组件然后再调用父组件的的方法,从而修改负组件中状态,并共享给其他组件,这个就是状态提升。
场景:
表单联动
全局状态管理

五十八、对 React 和 Vue 的理解,它们的异同
相同点:
组件化开发
响应式数据绑定
虚拟DOM
不同点:
创建方式:JSX HTML
数据流 单向数据流 数据双向绑定

五十九、对React SSR的理解
服务端渲染时把数据和模板组成的HTML一起返回。
优点:
对SEO友好
所有模板图片资源都存储到服务器
一个html返回所有的数据
减少了http请求
响应快、用户体验好
缺点:
服务器压力大
生命周期只会执行到componentDidMount之前

六十、react-redux中的connect怎么实现?
connect 是 react-redux 的核心 API,用于将 React 组件与 Redux store 连接起来。它的实现主要基于高阶组件(HOC)模式,通过订阅 store 的状态变化并映射到组件的 props
从 Context 获取 Store
connect 通过 React 的 context 获取由 Provider 注入的 Redux store。

映射 State 和 Dispatch

mapStateToProps(state, ownProps?): 将 store 的 state 映射到组件的 props。
mapDispatchToProps(dispatch, ownProps?): 将 action creators 绑定到 dispatch,并作为 props 传递给组件。
合并 Props
将 mapStateToProps、mapDispatchToProps 和组件自身的 ownProps 合并为最终传递给子组件的 props。

订阅 Store 变化
在组件的生命周期(如 componentDidMount)中订阅 store 的变化,并在状态更新时触发重新渲染。

返回高阶组件
connect 返回一个函数,该函数接收原始组件并返回一个包装后的新组件

function connect(mapStateToProps, mapDispatchToProps) {
  return function(WrappedComponent) {
    return class ConnectedComponent extends React.Component {
      static contextTypes = {
        store: PropTypes.object
      };

      componentDidMount() {
        this.unsubscribe = this.context.store.subscribe(() => {
          this.forceUpdate();
        });
      }

      componentWillUnmount() {
        this.unsubscribe();
      }

      render() {
        const { store } = this.context;
        const stateProps = mapStateToProps(store.getState(), this.props);
        const dispatchProps = mapDispatchToProps(store.dispatch, this.props);
        
        return (
          <WrappedComponent 
            {...this.props} 
            {...stateProps} 
            {...dispatchProps} 
          />
        );
      }
    };
  };
}

六十一、diff的时间复杂度是多少,为什么?
传统Diff算法因全局对比和复杂编辑操作导致O(n³)复杂度,而React、Vue等框架通过层级限制与节点复用策略将复杂度降至O(n),同时兼顾性能与开发效率

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容