React面试题总结

330 阅读31分钟

React的设计思想

  1. 组件化 :React 采用组件化的设计思想,可以将 UI 划分为一系列独立、可复用的组件,每个组件都有自己的状态和逻辑。这种方式可以使代码的复用性、可读性和维护性大大提高。

  2. 声明式编程:React 采用声明式编程模式,开发者只需要描述他们希望的最终状态,而不需要关心如何达到这个状态。这使得代码更加简洁,易于理解和预测。

  3. 虚拟 DOM:React 引入了虚拟 DOM 的概念,它是对真实 DOM 的轻量级表示。当组件的状态变化时,React 会创建一个新的虚拟 DOM,并与旧的虚拟 DOM 进行比较,然后只更新实际 DOM 中发生变化的部分。这种方式可以避免直接操作 DOM,从而提高性能。

  4. 单向数据流:React 采用单向数据流(也被称为单向绑定),父组件可以将属性(props)传递给子组件,但子组件不能修改接收到的 props。这种方式保证了数据的流动性和可预测性,也使得应用的状态管理变得更加容易。

  5. JSX:React 引入了 JSX,这是一种 JavaScript 和 XML 的混合语法,使得 JavaScript 中可以编写类似 HTML 的标记语法。这种方式可以使得组件的结构更加清晰,代码更加易于编写和理解。

  6. 钩子(Hooks):React Hooks 是 React 16.8 版本新增的特性,允许你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hooks 可以让你在现有的组件之间复用状态逻辑,而不需要改变你的组件结构,使得代码更加简洁,更易于管理和测试

原文链接:blog.csdn.net/m0_37609579… React设计模式

JSX是什么,它和JS有什么区别

JSX是react的语法糖,它允许在html中写JS,它不能被浏览器直接识别,需要通过webpack、babel之类的编译工具转换为JS执行

JSX与JS的区别:

  1. JS可以被打包工具直接编译,不需要额外转换,jsx需要通过babel编译,它是React.createElement的语法糖,使用jsx等价于React.createElement
  2. jsx是js的语法扩展,允许在html中写JS;JS是原生写法,需要通过script标签引入

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

只要使用了jsx,就需要引用react,因为jsx本质就是React.createElement

链接:juejin.cn/post/718238…

React 类组件的主要生命周期方法

React 组件的生命周期是指组件从被创建到销毁的整个过程。在这个过程中,React 会为组件提供一系列的“生命周期方法”(也称为“钩子”),这些方法允许我们在组件生命周期的不同阶段执行代码。了解并利用这些生命周期方法,可以帮助我们更好地控制组件的行为,比如数据获取、状态更新、DOM 操作等。

不过,值得注意的是,随着 React 的发展,特别是在 React 16.3 引入的 Hooks 之后,许多原本在类组件中通过生命周期方法实现的功能现在可以通过函数组件和 Hooks 来完成,使得代码更加简洁和灵活。尽管如此,理解类组件的生命周期仍然是学习 React 的重要一环。

  1. Mounting(挂载)

    • constructor(props):在组件被创建并即将被插入到 DOM 中时调用。常用于初始化 state 或绑定事件处理函数。
    • static getDerivedStateFromProps(props, state):React 16.3 引入,用于替代 componentWillReceiveProps。当组件实例化后和接收新的 props 时调用,返回一个对象来更新 state,或者返回 null 来表明新的 props 不需要任何 state 更新。
    • render():必需的方法,组件的输出应该通过这个方法返回。它应该是一个纯函数,即对于相同的输入,始终返回相同的输出,不修改组件的状态,并且不直接与浏览器交互。
    • componentDidMount():在组件被挂载后立即调用。这是执行副作用(如数据获取、订阅和手动更改 DOM)的好地方。
  2. Updating(更新)

    • shouldComponentUpdate(nextProps, nextState):在接收到新的 props 或 state 时调用,并返回一个布尔值。如果返回 false,则组件不会重新渲染。这通常用于性能优化。
    • static getSnapshotBeforeUpdate(prevProps, prevState):React 16.3 引入,在最新的渲染输出提交给 DOM 之前调用。它使你的组件能在它们被实际渲染之前捕获一些信息(如滚动位置)。
    • componentDidUpdate(prevProps, prevState, snapshot):在组件的更新被处理之后调用。这是执行副作用(如订阅)的好地方,但请注意,如果你需要在组件渲染后立即执行 DOM 操作,你应该在 render 方法中设置 ref,并在 componentDidMount 和 componentDidUpdate 中使用这些 ref。
  3. Unmounting(卸载)

    • componentWillUnmount():在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,如取消网络请求、移除订阅等。
  4. Error Handling(错误处理)

    • static getDerivedStateFromError(error):React 16.6 引入,在后代组件抛出错误后被调用。它接收那个错误作为参数,并返回一个值来更新 state。
    • componentDidCatch(error, errorInfo):在后代组件抛出错误后被调用。它接收两个参数:error(抛出的错误)和errorInfo(一个包含错误堆栈的对象)。这些参数可以被用来记录错误日志。

用Hooks代替React生命生命周期

1. componentDidMount 和 componentWillUnmount 替代

类组件中的componentDidMount通常用于执行数据获取、订阅或DOM操作,而componentWillUnmount用于执行清理操作,如取消订阅或移除事件监听器。

在函数组件中,你可以使用useEffect Hook来替代这两个生命周期方法。useEffect接受一个函数作为参数,该函数会在组件渲染到屏幕后执行(类似于componentDidMount),并且返回一个清理函数(如果提供的话),该清理函数会在组件卸载前执行(类似于componentWillUnmount)。


	import React, { useEffect } from 'react';  

	  
	function MyComponent() {  

	  useEffect(() => {  

	    // 类似于 componentDidMount  

	    console.log('Component did mount');  


	    // 假设有一个订阅函数  

	    const subscription = someLibrary.subscribeToNewData(() => {  

	      // 处理新数据  

	    });  

	  

	    // 返回一个清理函数  

	    return () => {  

	      // 类似于 componentWillUnmount  

	      console.log('Component will unmount');  

	      subscription.unsubscribe();  

	    };  

	  }, []); // 空依赖数组表示这个effect只在挂载和卸载时运行  

	  

	  return <div>Hello, World!</div>;  

	}

2. componentDidUpdate 替代

在类组件中,componentDidUpdate用于在组件更新后执行代码。在函数组件中,你可以通过传递依赖项数组给useEffect来实现类似的功能。当依赖项发生变化时,useEffect中的函数会重新执行。


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


	function MyComponent({ someProp }) {  

	  const [localState, setLocalState] = useState(null);  

	  useEffect(() => {  

	    // 类似于 componentDidUpdate,但只会在someProp或localState变化时执行  

	    console.log('Component did update');  

	  

	    // 可以在这里根据someProp或localState的变化来执行代码  

	  }, [someProp, localState]); // 依赖项数组  

	  

	  return <div>Hello, {someProp}!</div>;  

	}

3. shouldComponentUpdate 替代

虽然shouldComponentUpdate在类组件中用于控制组件是否应该重新渲染,但在函数组件中,你通常不需要直接控制这一点,因为React的Reactivity模型(通过Hooks如useStateuseReducer)已经为你处理了大部分的优化。

然而,如果你确实需要优化渲染性能,你可以使用React.memo来包装你的组件,或者利用useMemouseCallback来避免在每次渲染时都重新创建昂贵的对象或函数。

React的Hooks

React的Hooks是React 16.8版本引入的一项重要特性,它们允许在函数式组件中使用状态和其他React特性,而不需要使用类组件。Hooks为开发者提供了强大的工具,涵盖了状态管理、副作用处理、性能优化、DOM操作等各个方面。以下是一些常用的React Hooks及其用途:

  1. useState

    • 用途:用于在函数组件中添加React状态。通过传入一个初始状态,useState返回一个状态变量和一个更新该状态的函数。
    • 用法const [state, setState] = useState(initialState); 其中initialState是状态的初始值,它可以是任意类型的数据。
  2. useEffect

    • 用途:用于处理函数组件中的副作用操作,如数据获取、订阅或手动更改DOM。它可以看作是componentDidMountcomponentDidUpdatecomponentWillUnmount的组合。
    • 用法useEffect(effect, [dependencies]); 其中effect是包含副作用操作的函数,dependencies是一个包含依赖项的数组。当依赖项变化时,effect函数会重新执行。
  3. useContext

    • 用途:允许在函数组件中访问上下文(Context)。上下文是一种全局数据共享的方式,可以避免在多层嵌套的组件树中通过层层传递props。
    • 用法const value = useContext(MyContext); 其中MyContext是一个使用React.createContext()创建的上下文对象。
  4. useReducer

    • 用途:用于在函数组件中处理更复杂的状态逻辑,特别是当组件拥有多个状态或状态更新依赖于前一个状态时。
    • 用法const [state, dispatch] = useReducer(reducer, initialState); 其中reducer是一个函数,它根据当前状态和action来返回新的状态,initialState是状态的初始值。
  5. useCallback 一个允许你在多次渲染中缓存函数的 React Hook

    • 用途:返回一个记忆化的回调函数。当依赖项变化时,才会重新创建该回调函数,从而避免在渲染时创建不必要的新函数。
    • 用法const memoizedCallback = useCallback(() => { /* do something */ }, [dependencies]);
  6. useMemo

    • 用途:返回一个记忆化的值。它只会在其依赖项变化时重新计算。这对于执行昂贵的计算并避免在每次渲染时都重新计算它们很有用。
    • 用法const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  7. useRef

    • 用途:主要用于在函数组件中保存和访问可变的ref对象。useRef返回一个包含current属性的对象,其current属性被初始化为传入的参数(initialValue)。useRef返回的对象在组件的整个生命周期内保持不变。
    • 用法const refContainer = useRef(initialValue);
  8. useLayoutEffect

    • 用途:与useEffect类似,但它会在所有的DOM变更之后同步调用。如果你的副作用操作需要访问DOM元素,并且这些操作需要在浏览器绘制之前完成,那么useLayoutEffect是一个更好的选择。
    • 用法useLayoutEffect(() => { /* DOM操作 */ }, [dependencies]);
  9. useImperativeHandle

    • 用途:与forwardRef一起使用,它允许你自定义暴露给父组件的实例值。这在创建可复用的自定义Hooks时特别有用,尤其是当你想在父组件中直接访问子组件的方法或属性时。
    • 用法useImperativeHandle(ref, createHandle, [deps]); 其中refforwardRef传递的ref对象,createHandle是一个函数,用于创建需要暴露给父组件的值,deps是依赖项数组。
  10. useId

    • 用途useId 是一个 React Hook,可以生成传递给无障碍属性的唯一 ID。
    • 用法const passwordHintId = useId(); <p id={passwordHintId}> 不要使用 useId 来生成列表中的 key**。key 应该由你的数据生成

为什么不能在循环、条件或嵌套函数中调用Hooks?

  React依赖于Hooks调用的顺序来正确地关联Hook的状态。如果在循环或条件语句中调用Hooks,每次迭代或条件判断都可能产生新的Hook状态,这会导致React无法正确地关联状态和更新。

Redux是什么?

redux是一种用于管理JavaScript应用程序的状态管理库。它可以与React、Augular、Vue等前端框架结合使用,但也可以纯在JavaScript应用程序中独立使用。redux遵循单项数据流的原则,通过一个全局的状态树来管理应用程序的状态,从而使状态的变化更加可预测和已于维护。

原理

Redux的工作原理可以概括为以下几个关键概念:

  1. Store:Redux应用的状态被统一地存储在一个称为“store”的对象中。这个对象包含了整个应用的状态树,是唯一的真实数据源。
  2. Action:Action是一个包含有关操作信息的普通对象。它描述了要在应用中执行的操作,例如添加、删除或更新数据。Action是改变状态的唯一途径。
  3. Reducer:Reducer是一个纯函数,它接收当前的状态和一个Action作为参数,并返回一个新的状态。Reducer定义了状态的变化逻辑,确保状态的变化是可预测的。
  4. Dispatch:Dispatch是一个函数,用于将Action发送给Reducer以更新状态。通过调用dispatch(action),Redux会根据Action的类型找到对应的Reducer来更新状态。
  5. Subscribe:通过订阅(subscribe),可以监听状态的变化。每当状态发生变化时,订阅的回调函数会被触发,允许组件重新渲染以反映新的状态。

怎样使用

使用Redux通常涉及以下几个步骤:

  1. 创建Store:使用Redux提供的createStore函数来创建store。这个函数接受一个或多个reducer作为参数,并返回一个store对象。
  2. 定义Reducer:Reducer是一个纯函数,它根据当前的状态和传入的action返回一个新的状态。在Redux应用中,你需要定义多个reducer来处理不同的action类型,然后使用combineReducers函数将它们合并成一个根reducer。
  3. 派发Action:当需要改变应用的状态时,可以创建并派发一个action。这个action描述了要执行的操作和相关的数据。
  4. 在组件中使用Redux:在React等前端框架中,你可以使用react-redux库提供的connect函数来连接React组件和Redux store。这样,组件就可以通过props接收到最新的状态,并可以派发action来改变状态。

示例

以下是一个简单的Redux使用示例,展示了如何在一个React组件中使用Redux来管理状态:


	// 定义action类型  

	const ADD_TODO = 'ADD_TODO';  


	// 创建action  

	function addTodo(text) {  

	  return { type: ADD_TODO, text };  

	}  

	  

	// 定义reducer  

	function todoReducer(state = [], action) {  

	  switch (action.type) {  

	    case ADD_TODO:  

	      return [...state, { text: action.text, completed: false }];  

	    default:  

	      return state;  

	  }  

	}  

	  

	// 创建store  

	import { createStore } from 'redux';  

	const store = createStore(todoReducer);  

	  

	// 在React组件中使用Redux  

	import React from 'react';  

	import { connect } from 'react-redux';  

	  

	class TodoList extends React.Component {  

	  render() {  

	    return (  

	      <ul>  

	        {this.props.todos.map(todo =>  

	          <li key={todo.id}>  

	            {todo.text}  

	          </li>  

	        )}  

	      </ul>  

	    );  

	  }  

	}  

	  

	const mapStateToProps = state => ({  

	  todos: state  

	});  

	  

	export default connect(mapStateToProps)(TodoList);  

	  

	// 派发action  

	store.dispatch(addTodo('Learn Redux'));

React的Diff算法简述:

1.简述

  • 把树形结构按照层级分解,只比较同级元素。
  • 列表结构的每个单元添加唯一的 key 属性,方便比较。
  • React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
  • 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
  • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

2.相关概念

  1. 三大策略:采用Tree Diff、Component Diff、Element Diff策略,分别针对树层级、组件层级和元素层级进行优化。
  2. 层级控制:Tree Diff忽略跨层级操作,只比较同层级节点,降低复杂度至O(n)。
  3. 组件优化:Component Diff针对同类型组件进行子树比较,不同类型则直接替换,支持shouldComponentUpdate优化。
  4. 元素复用:Element Diff通过唯一key标识节点,实现高效复用和移动,减少不必要的DOM操作。

进一步阅读

React 16引入的Fiber架构

React 16引入了Fiber架构,这是一种全新的协调引擎,通过增量渲染(拆分成多个小任务执行)、优先级调度(确保高优先级任务快速响应)和可中断恢复(允许在任务之间暂停和恢复)等机制,提高了React的性能和响应速度,使其能够更高效地处理复杂和高频的用户交互。

  1. 背景

    • 在React 16之前,React的渲染过程是通过一个称为“Stack Reconciler”的递归过程来完成的。这个过程在大型项目或复杂组件树中可能导致性能问题,因为它会长时间占用浏览器的主线程,导致页面卡顿和用户体验下降。
  2. Fiber架构的引入

    • 为了解决这些问题,React 16引入了Fiber架构。Fiber是一个将渲染工作分成多个小的工作单元(称为“Fiber”)的架构,这些工作单元可以在多个帧中执行。这种拆分使得React能够在工作单元之间暂停和恢复,从而避免长时间占用主线程。
  3. 主要特点

    • 增量渲染:Fiber将渲染工作分成多个小的工作单元,这些单元可以在多个帧中执行。由于分成了更小的任务单元,在这些任务单元之间可以停顿,从而允许浏览器执行其他任务,如用户输入或动画。
    • 优先级调度:不同的任务根据其重要性被赋予不同的优先级。例如,用户输入等高优先级任务可以快速响应,而低优先级的任务(如数据获取)则可以在主线程空闲时执行。
    • 恢复和暂停:React可以在处理完一个工作单元后中断,检查是否有更高优先级的任务需要处理。如果有,则优先处理高优先级任务;否则,继续处理剩余的渲染任务。
  4. 两阶段 & 两棵树

    • 调度阶段(Reconciliation Phase) :在这一阶段,React会计算需要更新的组件和对应的状态变更。这个阶段是可以被中断的,React会根据优先级逐步处理更新任务。
    • 提交阶段(Commit Phase) :一旦调度阶段完成,提交阶段会将更新应用到实际的DOM中。这个阶段是同步的,React会确保所有的DOM变更在一次帧内完成。
    • 两棵树:Fiber架构在内存中包含了两棵树,一棵是当前树(已经渲染完成的树),另一棵是工作树(Work-in-Progress Tree)。这两棵树互相替换进行状态的更新。
  5. 实现方式

    • React重新设计了一种链表的vdom结构,每个节点称之为一个Fiber。每个Fiber包含了一些属性,如stateNode(状态节点)、child(子节点)、return(表示当前节点处理完毕后,应该向谁提交自己的成果,即effect list)、sibling(兄弟节点)等。这种链表结构使得每一个Fiber都可以访问到整棵树。
  6. 性能提升

    • 通过Fiber架构,React能够更好地处理复杂和高频的用户交互,减少页面卡顿,提升用户体验。特别是对于动画和交互频繁的应用场景,Fiber架构能够带来显著的性能提升。

综上所述,React 16引入的Fiber架构通过增量渲染、优先级调度、恢复和暂停等机制,实现了更细粒度的更新和高效的调度,从而提升了React的性能和用户体验。

useMemouseCallback的区别。

1. 功能不同

  • useMemo:用于记忆化(缓存)计算结果。它接收一个计算函数和一个依赖项数组作为参数。只有当依赖项数组中的值发生变化时,它才会重新执行计算函数并返回新的结果。如果依赖项没有变化,它将返回上一次的计算结果,从而避免不必要的计算。
  • useCallback:用于记忆化(缓存)回调函数。它接收一个回调函数和一个依赖项数组作为参数。只有当依赖项数组中的值发生变化时,它才会返回一个新的回调函数实例。如果依赖项没有变化,它将返回之前缓存的回调函数实例,从而避免在每次渲染时都创建新的函数实例。

2. 返回值类型不同

  • useMemo:返回一个记忆化的计算结果。
  • useCallback:返回一个记忆化的回调函数。

3. 使用场景不同

  • useMemo:主要用于优化计算操作,避免在每次渲染时都进行昂贵的计算。例如,当需要根据组件的props或state计算出一个新的值时,可以使用useMemo来缓存这个计算结果。
  • useCallback:主要用于优化传递给子组件的回调函数,避免在每次父组件渲染时都传递一个新的函数给子组件,从而防止不必要的子组件渲染。这在使用React.memo或shouldComponentUpdate等优化手段时特别有用。

4. 依赖项数组的作用

  • 对于useMemouseCallback来说,依赖项数组都起着至关重要的作用。它告诉React哪些值的变化会触发重新计算或重新创建。如果省略依赖项数组或传递空数组,则计算函数或回调函数只会在组件首次渲染时执行或创建一次,之后将不再变化。

拓展知识
useCallback 是「useMemo 的返回值为函数」时的特殊情况,是 React 提供的便捷方式。在 React Server Hooks 代码 中,useCallback 就是基于 useMemo 实现的。尽管 React Client Hooks 没有使用同一份代码,但 useCallback 的代码逻辑和 useMemo 的代码逻辑仍是一样的。

说说React的事件机制?

在React中,基于浏览器事件有一套自身的事件机制,包括:事件注册、事件合成、事件派发等,这些事件被称为合成事件。它的所有事件都是挂载在Document对象上,当真实DOM触发时,会冒泡到document上后,再处理react事件,所以会先执行原生事件,再处理react事件,最后再真正执行document上挂载事件。

React的事件机制通过事件代理、合成事件、事件池等技术,提供了一种高效、标准化且跨浏览器兼容的事件处理方式。这使得开发者可以更加专注于业务逻辑的实现,而不必担心浏览器之间的差异和性能问题。

说说React的性能优化?

1. 减少不必要的渲染

  • 优化shouldComponentUpdate或PureComponent:对于类组件,可以通过实现shouldComponentUpdate方法或继承PureComponent来避免不必要的重新渲染。这些方法通过比较props和state的变化来决定是否需要重新渲染组件。

  • 使用React.memo:对于函数组件,可以使用React.memo进行包裹,它仅对props进行浅比较,如果props没有变化,则不会重新渲染组件。

  • 使用React.useCallback绑定函数以避免不必要的重复定义。

  • 使用React.useMemo来记住计算过的值。

2. 列表渲染优化

  • 使用唯一的key属性:在列表渲染时,为每个列表项指定一个唯一的key属性,帮助React识别每个元素的身份,减少重复渲染和操作的开销。
  • 虚拟列表:对于大量数据的列表,可以使用虚拟列表技术(如react-windowreact-virtualized),只渲染可视区域内的列表项,以提高渲染性能和滚动性能。

3. 懒加载和代码分割

  • 懒加载组件:对于非首屏加载就需要的组件,可以使用懒加载技术(如React.lazySuspense),按需加载它们,减少初始渲染体积和提高首屏加载速度。
  • 代码分割:通过Webpack等构建工具实现代码分割,将应用拆分成多个小块,根据需要加载对应的代码块。

4. 合理使用状态管理

  • 选择合适的状态管理库:根据项目的规模和复杂度,选择合适的状态管理库(如Redux、MobX或Context API)。
  • 避免不必要的全局状态:尽量保持组件的状态局部化,减少不必要的全局状态管理,以降低应用的复杂度和提升性能。

5. 优化事件处理

  • 避免在render方法中绑定事件处理函数:在render方法中绑定事件处理函数会导致每次渲染时都创建新的函数实例,从而增加内存使用和性能开销。可以将事件处理函数定义在组件外部或作为类成员方法。

6. 使用React的并发模式(Concurrent Mode)

  • 优先级调度和递增式渲染:并发模式通过引入优先级调度和递增式渲染等机制,使得React可以更好地管理和分配任务,以实现更平滑的用户体验。
  • 使用useTransition和useDeferredValue:这两个Hooks可以帮助开发者更好地控制任务的优先级和延迟非紧急任务的执行,以优化应用的响应性和性能。

7. 其他优化策略

  • 减少组件嵌套:避免过深的组件嵌套,以降低diff算法的复杂度和渲染时间。
  • 选择合适的第三方库和插件:避免引入过多冗余的库和插件,以减少性能开销。
  • 使用生产环境构建:确保在部署到生产环境时使用了React的生产版本,它会进行代码压缩和性能优化。

8. 使用服务端渲染(SSR)或静态站点生成(SSG)。

9. 使用Webpack的Tree-Shaking特性去除无用代码。

10. 使用性能工具(如React DevTools Profiler)分析组件渲染性能。

React18有哪些更新

React 18的更新主要包括并发模式、‌浏览器最低兼容性提升、‌新的根API、‌异步任务批处理、‌转换更新API等。‌

1. 并发模式(Concurrent Mode)

  • 全面开启并发模式:React 18全面开启了并发模式,使得React能够同时准备多个版本的UI,并且能够在渲染过程中中断、恢复、中止或插入高优先级的任务。这种机制可以显著提升应用的响应性和性能。
  • 新的根节点挂载方式:引入了ReactDOM.createRoot()来替代ReactDOM.render()创建根元素进行渲染。使用createRoot()会自动启用并发模式,而render()则保留为旧版模式的入口。

2. 新的API和Hooks

  • startTransition:一个新的API,用于将某些状态更新标记为可以延迟的非紧急更新。这对于优化长时间运行的任务(如数据加载)特别有用,因为它允许React在渲染这些更新之前优先处理用户的交互。
  • useTransition:一个与startTransition对应的Hook,用于在函数组件中更方便地控制过渡更新的状态。它返回一个包含挂起状态标志(isPending)和启动过渡的函数(类似于startTransition)的数组。
  • useDeferredValue:一个Hook,用于获取一个值的“延迟”版本,该值通过过渡任务得到。这对于在输入时延迟更新某些非紧急状态特别有用,可以减少不必要的渲染,提高性能。
  • useId:一个新的Hook,用于生成在客户端和服务端两侧都独一无二的ID,避免在服务器渲染的应用中由于内容不匹配导致的错误。
  • useSyncExternalStore:一个允许使用第三方状态管理库来支持并发模式的Hook。它消除了对useEffect的依赖,使得状态管理更加高效和灵活。
  • useInsertionEffect:一个供CSS-in-JS库使用的Hook,用于在DOM变更发生后但在布局计算之前注入样式,以解决性能问题。

3. setState的异步和同步行为

  • 默认异步更新:在React 18中,setState默认以异步方式进行更新,这有助于减少不必要的重渲染并提高性能。但是,在某些情况下,你可能需要立即获取更新后的状态,这时可以使用flushSync方法来实现同步更新。
  • flushSync:一个方法,用于强制React以同步方式执行setState调用。但是,需要注意的是,使用flushSync可能会对性能产生影响,因此应谨慎使用。

4. 自动批处理的功能

React 17 不会在事件处理程序之外进行批处理,比如不会在一个 promise 中合并处理 setState

批处理是指,当 React 在一个单独的重渲染事件中批量处理多个状态更新以此实现优化性能。如果没有自动批处理的话,我们仅能够在 React 事件处理程序中批量更新。在 React 18 之前,默认情况下 promise、setTimeout、原生应用的事件处理程序以及任何其他事件中的更新都不会被批量处理;但现在,这些更新内容都会被自动批处理:

从 React 18 开始createRoot,所有更新都将自动批处理,无论它们来自何处。

这意味着超时、promise、本机事件处理程序或任何其他事件内部的更新将以与 React 事件内部的更新相同的方式进行批处理。我们希望这会减少渲染工作,从而提高应用程序的性能:

原文链接:blog.csdn.net/qq_16546829…

5. 移除对IE浏览器的支持

  • React 18不再支持Internet Explorer(IE)浏览器。这是React团队为了推动现代Web开发并减少维护老旧浏览器的负担而做出的决定。

react 中并发模式和 fiber 有什么关系?

React Fiber 架构: React Fiber 是 React 16 中引入的新的核心算法,旨在提高 React 的可扩展性和响应性。 在 Fiber 架构中,React 将更新过程拆分成多个较小的任务(或称为“fibers”),这使得 React 可以在主线程上进行更精细的调度。 这种架构允许 React 在长时间运行的任务中进行中断和恢复,从而使主线程可以处理其他高优先级任务,如用户输入或动画。

并发模式(Concurrent Mode): 并发模式是 React 18 中引入的一种新模式,它建立在 Fiber 架构之上。 在并发模式下,React 可以将渲染工作切分为多个较小的任务,并根据优先级在主线程上进行调度。这允许页面在渲染过程中保持响应性,特别是在低端设备或大型应用程序中。 并发模式还引入了新的特性,如过渡模式(Transition Mode)和延迟渲染(Deferred Rendering),这些都有助于提高应用的性能和响应性。 关系:

Fiber 架构为并发模式提供了基础。没有 Fiber 架构的细粒度任务调度能力,并发模式是不可能实现的。 并发模式是 Fiber 架构的一个高级应用,它充分利用了 Fiber 架构的特性,使得 React 可以在保持应用响应性的同时处理复杂的 UI 更新。 3.3. 并发和并行 并发:指应用能够交替执行不同的任务,其实并发有点类似于多线程的原理,多线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已.

就类似于你,吃一口饭喝一口水,以正常速度来看,完全能够看的出来,当你把这个过程以n倍速度执行时…可以想象一下.

并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话,这两件事情可以同时执行

18 版本之前 没有并发模式 fiber 主要有什么作用?

一句话理解:无并发模式之前 fiber 可以让任务暂停并根据优先级执行 并支持异步渲染,有并发模式 可以让任务交替执行

任务拆分与可中断性:在之前的 React 版本中,组件的渲染和更新是递归进行的,且不可中断。这在处理大型或复杂的 UI 时可能导致长时间的阻塞,从而影响应用的响应性。Fiber 架构通过将渲染任务拆分成更小的单元(即 "fibers"),允许 React 在执行过程中中断当前任务,转而处理其他更高优先级的任务,如用户交互或动画。 优先级调度:Fiber 架构引入了一个调度器(Scheduler),它可以根据任务的优先级来管理任务的执行顺序。高优先级的任务可以打断低优先级的任务,确保重要的更新能够尽快得到处理,从而提高应用的响应性和用户体验。 异步渲染与更新:Fiber 架构为异步渲染和更新铺平了道路。通过将渲染任务切分,React 可以在主线程空闲时逐步完成这些任务,而不是一次性同步完成所有渲染工作。这有助于减少页面在更新时的卡顿现象,提升用户界面的流畅性。 为并发模式做准备:虽然 React 16 中还没有正式的并发模式,但 Fiber 架构的引入是为后续版本中实现并发模式做技术铺垫。它改变了 React 的工作方式,使得未来的并发渲染成为可能。

原文链接:blog.csdn.net/qq_16546829…

React-Router

zhuanlan.zhihu.com/p/705970625

React-Router 是 React 的一个路由库,它允许开发者在 React 应用中方便地添加视图和数据流,同时保持页面与 URL 的同步。React-Router 提供了完整的路由解决方案,帮助开发者构建单页应用(SPA)。以下是关于 React-Router 的详细介绍:

一、React-Router 的组成

React-Router 包含三个主要的库:

  • react-router:提供核心的路由组件与函数。
  • react-router-dom:提供浏览器运行环境所需的特定组件,如 <BrowserRouter><HashRouter><Link><NavLink><Route><Routes> 等。
  • react-router-native:提供 React Native 运行环境所需的特定组件。

在实际开发中,通常会根据应用运行的环境选择安装 react-router-dom 或 react-router-native

1. 使用

在项目中,通常会将 <BrowserRouter> 或 <HashRouter> 作为应用的根组件包裹整个应用,以开启路由功能。然后,使用 <Routes> 包裹所有的 <Route>,并通过 <Route> 配置路径和组件的映射关系。

import React from 'react';  
import ReactDOM from 'react-dom/client';  
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';  
  
import Home from './Home';  
import About from './About';  
  
const root = ReactDOM.createRoot(document.getElementById('root'));  
  
root.render(  
  <Router>  
    <div>  
      <nav>  
        <ul>  
          <li><Link to="/">Home</Link></li>  
          <li><Link to="/about">About</Link></li>  
        </ul>  
      </nav>  
  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="/about" element={<About />} />  
      </Routes>  
    </div>  
  </Router>  
);

2、React-Router 的核心组件

React-Router 提供了多个核心组件,用于实现路由功能:

  • <BrowserRouter> 和 <HashRouter> :监听路径的改变,并将相应的路径传递给子组件。<BrowserRouter> 使用 HTML5 的 History API 来处理 URL,而 <HashRouter> 则通过 URL 的 hash 部分来处理。
  • <Routes> :用来包裹所有的 <Route>,匹配其中的一个路由。
  • <Route> :用来配置路径和组件的映射关系。当路径与当前 URL 匹配时,渲染对应的组件。
  • <Link>  和  <NavLink> :用于声明路由要跳转的地方。<NavLink> 还可以实现导航的“高亮”效果。
  • <Navigate> :用于修改 URL,并切换视图。它相当于编程式的路由跳转。

React-Router 的版本差异

React-Router 的不同版本之间存在一些差异,尤其是在组件和语法上。例如,React-Router v6 移除了 <Switch> 组件,并引入了 <Routes> 和 <Route> 的新用法。同时,component 属性也被替换为 element 属性,以支持 JSX 元素。

SetState是同步还是异步的

setState是一个异步方法,但是在setTimeout/setInterval等定时器里逃脱了React对它的掌控,变成了同步方法

实现机制类似于vue的$nextTick和浏览器的事件循环机制,每个setState都会被react加入到任务队列,多次对同一个state使用setState只会返回最后一次的结果,因为它不是立刻就更新,而是先放在队列中,等时机成熟在执行批量更新。

React18以后,使用了createRoot api后,所有setState都是异步批量执行的,但如果你需要在某些情况下强制同步执行setState(例如,在测试或特定性能敏感的场景中),你可以使用flushSync函数。flushSync是React 18引入的一个新API,它允许你将传递给它的回调函数中的setState调用视为一个单独的更新批次,并立即执行这些更新。

为什么虚拟 dom 会提高性能?

虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。

具体实现步骤如下:

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中

当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异

把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

参考链接: 如何理解虚拟DOM?