【React】精选10题

1.React Hooks带来了什么便利?

React Hooks是React16.8版本中引入的新特性,它带来了许多便利。

  1. 更简单的状态管理
    使用useState Hook可以在函数组件中方便地管理状态,避免了使用类组件时需要继承React.Component的繁琐操作。

  2. 避免使用类组件:函数式组件的书写方式更加简单、直观,避免了类组件中this指针的混乱问题。

  3. 更少的重复代码:使用useEffect Hook可以方便地实现数据获取、DOM操作等副作用相关的操作,避免了在不同的生命周期函数中重复编写相似的代码。

  4. 更好的代码复用:自定义Hook可以将一些可复用的逻辑封装起来,方便在不同的组件中复用。

  5. 更好的逻辑分离:使用useContext、useReducer和自定义Hook等可以帮助将逻辑分离到独立的模块中,提高代码的可维护性和可扩展性。

总之,React Hooks带来了更加简单、直观、高效的编程方式,可以让开发者更加专注于组件的逻辑实现。使得React的函数组件也具备了类组件的一些特性。

2. 列举几个常见的 Hook?

  1. useState Hook:用于在函数组件中管理状态,可以通过调用useState Hook来声明一个状态变量和更新函数,例如:

    const [count, setCount] = useState(0);
    
  2. useEffect Hook:用于在函数组件中处理副作用,可以传入一个回调函数和一个依赖数组,例如:

    useEffect(() => {
          
          
      // 处理副作用
    }, [dependency]);
    
  3. useContext Hook:用于在函数组件中访问React Context中的值,例如:

3.1. 在MyContext.js中定义MyContext上下文对象:

import {
    
     createContext } from 'react';

const MyContext = createContext();
export default MyContext;

3.2. 在App.js中使用MyContext.Provider包裹Child组件,传递要共享的数据

import MyContext from './MyContext';
import Child from './Child';

function App() {
    
    
  const data = 'hello world';
  return (
    <MyContext.Provider value={
    
    data}>
      <Child />
    </MyContext.Provider>
  );
}

3.3 在Child.js中使用useContext函数获取到MyContext传递的值:

import MyContext from './MyContext';

function Child() {
    
    
  const data = useContext(MyContext);
  return <h1>{
    
    data}</h1>;
}

export default Child;
  1. useReducer Hook:用于在函数组件中管理复杂状态,可以将一个reducer函数和初始状态传入useReducer Hook,返回一个状态和派发更新的函数,例如:
import React, {
    
     useReducer } from 'react';

const initialState = {
    
     count: 0 };

function reducer(state, action) {
    
    
 switch (action.type) {
    
    
   case 'increment':
     return {
    
     count: state.count + 1 };
   case 'decrement':
     return {
    
     count: state.count - 1 };
   default:
     throw new Error();
 }
}

function Counter() {
    
    
 const [state, dispatch] = useReducer(reducer, initialState);

 return (
   <div>
     Count: {
    
    state.count}
     <button onClick={
    
    () => dispatch({
    
     type: 'increment' })}>+</button>
     <button onClick={
    
    () => dispatch({
    
     type: 'decrement' })}>-</button>
   </div>
 );
}
  1. useCallback Hook:用于在函数组件中缓存回调函数,避免因为每次渲染都重新创建回调函数导致子组件重复渲染,例如:
import React, {
    
     useState, useCallback } from 'react';

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

  const handleClick = useCallback(() => {
    
    
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {
    
    count}</p>
      <button onClick={
    
    handleClick}>Increment Count</button>
    </div>
  );
}

在这个示例中,我们使用了 useCallback Hook 缓存了 handleClick 函数。handleClick 会在点击按钮时被调用,并将 count 的值加 1。我们将 count 作为依赖数组传入 useCallback 中,确保每次 count 发生变化时,handleClick 函数都会被更新。

使用 useCallback Hook 可以避免在每次渲染时都创建新的函数引用,从而提高性能。这对于传递给子组件的回调函数尤其有用,确保子组件不会不必要地重新渲染。同时,也可以使用 useMemo Hook 缓存计算结果,从而进一步提高性能。

  1. useMemo Hook:提供的一个 Hook,用于缓存计算结果,避免在每次渲染时都重新计算,从而提高性能。它接收一个计算函数和一个依赖数组作为参数,返回缓存的计算结果。
    例如:
import React, {
    
     useMemo, useState } from 'react';

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

  const expensiveCalculation = useMemo(() => {
    
    
    console.log('Calculating...');
    let result = 0;
    for (let i = 0; i < count * 1000000; i++) {
    
    
      result += i;
    }
    return result;
  }, [count]);

  return (
    <div>
      <p>Count: {
    
    count}</p>
      <p>Expensive Calculation: {
    
    expensiveCalculation}</p>
      <button onClick={
    
    () => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}
  1. useRef: 获取组件的真实节点或存储一些不常更新的数据,这些数据不受组件重新渲染影响。
  • 获取真实节点
const ref = useRef(null)
<div ref ={
    
    ref} > </div>
// 设置真实节点属性 .current为固定用法
ref.current.style.color = 'red'
  • 存储数据
const ref_obj = useRef({
    
    
	name:'icy',
	age:23
})
// 改变存储的数据
ref.obj.current.name = 'icy-godlike'

还有其他常用的Hook函数,如useImperativeHandle、useLayoutEffect等,开发者可以根据具体的需求选择不同的Hook函数。

3. 使用React Hooks有什么优势?

简化组件的复杂度:React Hooks可以帮助组件更加简洁明了,避免类组件中的一些复杂的生命周期函数。

更容易共享逻辑:React Hooks可以将组件中的状态和逻辑提取出来,通过自定义Hook在不同的组件中进行共享。

更容易测试:React Hooks可以将状态和逻辑的处理分离,使得测试变得更容易。

更好的性能:React Hooks可以避免类组件中因为响应式更新造成的额外渲染,从而提高应用的性能。

更快的开发速度:React Hooks能够帮助开发者更快地构建出复杂的UI组件,从而提高开发效率。

4. 简单介绍下React中diff算法?

在 React 中,当组件的状态发生变化时,React 会重新渲染组件。为了提高渲染效率,React 会使用一种叫做 diff 算法(也称为协调算法)来计算出哪些部分需要更新,哪些部分不需要更新。

简单来说,diff 算法就是比较两棵树的差异,然后将差异应用到真实的 DOM 树上,从而实现只更新必要的部分,避免全量更新。

React 中的 diff 算法具体实现如下:

对比两棵树的根节点,如果类型不同,则直接替换整个节点及其子节点,不再进行进一步比较;如果类型相同,则进入下一步。

对比节点的属性,如果发生变化,则更新节点的属性;如果没有变化,则进入下一步。

对比子节点,React 使用一种叫做 key 的机制来判断哪些节点需要更新,哪些节点需要删除,哪些节点需要新增。如果节点的 key 相同,则认为是同一节点,进行进一步比较;如果节点的 key 不同,则直接替换整个节点及其子节点,不再进行进一步比较。

对比完成后,React 会根据计算出的差异,生成一份更新计划(也称为变更记录),然后根据这份更新计划,进行 DOM 操作,将变更应用到真实的 DOM 树上。

通过使用 diff 算法,React 可以避免全量更新,从而提高渲染效率,使得 React 在大规模数据渲染的场景下仍然能够保持流畅的性能。

5. React中,能否直接将 props 的值复制给 state?

可以,但是应该避免这种写法:

constructor(props) {
    
    
 super(props);
 // 不要这样做
 this.state = {
    
     color: props.color };
}

只有在初始化组件状态时才能这样做。在组件的生命周期中,props 的值是不会自动更新到 state 中的,因此如果需要在组件运行时更新 state,需要使用setState()方法。

6. React Hooks当中的useEffect是如何区分生命周期钩子的

useEffect 钩子函数可以接收两个参数,第一个参数是一个函数,称为 effect 函数,第二个参数是一个数组,称为依赖项数组。

在 React 中,每一个组件都有不同的生命周期钩子,例如 componentDidMount,componentDidUpdate,componentWillUnmount 等。useEffect 钩子函数可以在一个函数中处理这些不同的生命周期钩子。

当使用 useEffect 钩子函数时,React 会自动根据参数来判断当前需要使用哪个生命周期钩子。

当依赖项数组为空时,useEffect 钩子函数的行为类似于 componentDidMount 和 componentWillUnmount 的结合体,即只在组件挂载时执行一次,并在组件卸载时执行清理操作。

当依赖项数组不为空时,useEffect 钩子函数的行为类似于 componentDidUpdate,即在组件挂载时执行一次,之后每次依赖项发生变化时都会执行一次,最后在组件卸载时执行清理操作。

因此,useEffect 钩子函数的行为会根据传入的依赖项数组的变化而变化,从而实现了不同的生命周期钩子的功能。

useEffect(() => {
    
    
   console.log('mounted'); // 依赖数组为[]时,componentDidMount | 依赖数组不为空,且发生变化时 componentDidUpdate
   
   return () => {
    
    
       console.log('willUnmount'); //  依赖数组为[]时,componentWillUnmount 
   }
 }, [source]);

7. 为什么不能用数组下标来作为react组件中的key?

在 React 中,key 属性用于标识组件在列表中的顺序,并用于优化组件的重新渲染。key 必须是唯一的,并且在组件列表中始终保持不变。

不能使用数组下标作为 key 属性是因为数组下标与组件的实际内容没有关系。例如,如果你在一个列表中删除第一个元素,React 将重新生成列表中所有元素的 key,因为数组下标已经改变,导致 React 需要重新渲染所有组件。

此外,使用数组下标作为 key 属性可能会导致错误的渲染结果。例如,如果你有两个组件,它们的 key 属性分别为 0 和 1,并且你想交换它们的顺序,则交换它们在数组中的位置可能会导致 key 属性被错误地匹配到错误的组件上,从而导致渲染错误。

因此,为了避免以上问题,key 属性应该是与组件的实际内容相关的唯一标识符,例如组件在数据库或服务端的 ID。

8. React Fiber是什么?

React Fiber 是 React 16 中引入的新的协调引擎。React Fiber 是一种新的、可中断的调度算法,可以更好地支持渐进式渲染、处理大型组件树以及优化代码的可维护性。

React Fiber 的主要目标是提高 React 应用程序的性能和交互性。具体来说,React Fiber 可以:

实现异步渲染,将渲染任务分割成多个小任务,并调度它们的执行顺序,以提高应用程序的响应性和流畅性。
支持可中断的渲染,允许 React 在执行长时间的计算或等待异步数据时中断渲染,并在后台执行其他任务,以避免应用程序的停顿或卡顿。
支持渐进式渲染,允许 React 逐步地将组件树渲染到屏幕上,从而更快地响应用户输入,并提高应用程序的交互性。
改进错误处理和调试功能,使得开发人员可以更轻松地调试和修复错误。
总之,React Fiber 是 React 16 中的一个重要特性,可以显著提高 React 应用程序的性能、交互性和可维护性。

9. 虚拟DOM一定更快吗?

虚拟 DOM 并不一定比直接操作 DOM 更快,因为在生成虚拟 DOM 的过程中也需要进行计算和操作。但是,使用虚拟 DOM 有以下几个优点:

减少不必要的操作:由于 React 使用了虚拟 DOM,可以将多次 DOM 操作合并为一次,从而减少了不必要的 DOM 操作,提高了性能。
避免重复渲染:React 会比较新旧虚拟 DOM 树的差异,只更新差异部分对应的真实 DOM,避免了重复渲染整个组件,提高了性能。
跨平台支持:虚拟 DOM 是 React 在 Web、Native 等多个平台都可以使用的重要原因之一。
当然,在某些场景下,直接操作 DOM 也可能比使用虚拟 DOM 更快,例如非常简单的组件或需要频繁更新的组件。但总的来说,在大多数情况下,使用虚拟 DOM 可以提高 React 应用程序的性能和可维护性。

10. 不同版本的 React 都做过哪些优化?

React 16 引入了 Fiber 架构,以提高应用程序的性能和交互性。
React 16.3 引入了新的 Context API,以更方便地共享数据和状态。
React 16.6 引入了 memo 和 lazy 函数,以更有效地处理组件性能和代码分离。
React 16.8 引入了 Hooks,以更方便地处理组件之间的状态和逻辑。
React 17 引入了新的事件系统以及一些其他的优化,例如对异常的处理方式等等。
除此之外,React 还对一些其他的细节进行了优化,例如对组件生命周期方法的改进、对 Virtual DOM 的优化、对 React Native 的优化等等。

React渲染页面的两个阶段:

调度阶段(reconciliation):在这个阶段 React 会更新数据生成新的 Virtual DOM,然后通过Diff算法,快速找出需要更新的元素,放到更新队列中去,得到新的更新队列。
渲染阶段(commit):这个阶段 React 会遍历更新队列,将其所有的变更一次性更新到DOM上。

React 15 架构

React15架构可以分为两层:

Reconciler(协调器)—— 负责找出变化的组件;
Renderer(渲染器)—— 负责将变化的组件渲染到页面上;
在React15及以前,Reconciler采用递归的方式创建虚拟DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,递归更新时间超过了16ms,用户交互就会卡顿。

为了解决这个问题,React16将递归的无法中断的更新重构为异步的可中断更新,由于曾经用于递归的虚拟DOM数据结构已经无法满足需要。于是,全新的Fiber架构应运而生。

React 16 架构

为了解决同步更新长时间占用线程导致页面卡顿的问题,也为了探索运行时优化的更多可能,React开始重构并一直持续至今。重构的目标是实现Concurrent Mode(并发模式)。

从v15到v16,React团队花了两年时间将源码架构中的Stack Reconciler重构为Fiber Reconciler。

React16架构可以分为三层:

Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler;
Reconciler(协调器)—— 负责找出变化的组件:更新工作从递归变成了可以中断的循环过程。Reconciler内部采用了Fiber的架构;
Renderer(渲染器)—— 负责将变化的组件渲染到页面上。

React 17 优化

React16的expirationTimes模型只能区分是否>=expirationTimes决定节点是否更新。React17的lanes模型可以选定一个更新区间,并且动态的向区间中增减优先级,可以处理更细粒度的更新。

Lane用二进制位表示任务的优先级,方便优先级的计算(位运算),不同优先级占用不同位置的“赛道”,而且存在批的概念,优先级越低,“赛道”越多。高优先级打断低优先级,新建的任务需要赋予什么优先级等问题都是Lane所要解决的问题。

Concurrent Mode的目的是实现一套可中断/恢复的更新机制。其由两部分组成:

一套协程架构:Fiber Reconciler
基于协程架构的启发式更新算法:控制协程架构工作方式的算法

猜你喜欢

转载自blog.csdn.net/qq_43682422/article/details/132227175