一文带你梳理React面试题(2023年版本)

2023-11-05

前言

在这里插入图片描述

一、React18有哪些更新?

  1. setState自动批处理

    在react17中,只有react事件会进行批处理,原生js事件、promise,setTimeout、setInterval不会
    react18,将所有事件都进行批处理,即多次setState会被合并为1次执行,提高了性能,在数据层,将多个状态更新合并成一次处理(在视图层,将多次渲染合并成一次渲染)

  2. 引入了新的root API,支持new concurrent renderer(并发模式的渲染)

    //React 17
    import React from "react"
    import ReactDOM from "react-dom"
    import App from "./App"
    
    const root = document.getElementById("root")
    ReactDOM.render(<App/>,root)
    
    // 卸载组件
    ReactDOM.unmountComponentAtNode(root)  
    
    // React 18
    import React from "react"
    import ReactDOM from "react-dom/client"
    import App from "./App"
    const root = document.getElementById("root")
    ReactDOM.createRoot(root).render(<App/>)
    
    // 卸载组件
    root.unmount()  
    
  3. 去掉了对IE浏览器的支持,react18引入的新特性全部基于现代浏览器,如需支持需要退回到react17版本

  4. flushSync
    批量更新是一个破坏性的更新,如果想退出批量更新,可以使用flushSync

    import React,{useState} from "react"
    import {flushSync} from "react-dom"
    
    const App=()=>{
      const [count,setCount]=useState(0)
      const [count2,setCount2]=useState(0)
    
      return (
        <div className="App">
          <button onClick=(()=>{
            // 第一次更新
            flushSync(()=>{
              setCount(count=>count+1)
            })
            // 第二次更新
            flushSync(()=>{
              setCount2(count2=>count2+1)
            })
          })>点击</button>
          <span>count:{count}</span>
          <span>count2:{count2}</span>	
        </div>	
      )
    }
    export default App
    
    
  5. react组件返回值更新

    • 在react17中,返回空组件只能返回null,显式返回undefined会报错
    • 在react18中,支持null和undefined返回
  6. strict mode更新
    当你使用严格模式时,React会对每个组件返回两次渲染,以便你观察一些意想不到的结果,在react17中去掉了一次渲染的控制台日志,以便让日志容易阅读。react18取消了这个限制,第二次渲染会以浅灰色出现在控制台日志

  7. 支持useId
    在服务器和客户端生成相同的唯一一个id,避免hydrating的不兼容

  8. useSyncExternalStore
    用于解决外部数据撕裂问题

  9. useInsertionEffect
    这个hooks只建议在css in js库中使用,这个hooks执行时机在DOM生成之后,useLayoutEffect执行之前,它的工作原理大致与useLayoutEffect相同,此时无法访问DOM节点的引用,一般用于提前注入脚本

  10. Concurrent Mode
    并发模式不是一个功能,而是一个底层设计。

    它可以帮助应用保持响应,根据用户的设备性能和网速进行调整,它通过渲染可中断来修复阻塞渲染机制。在concurrent模式中,React可以同时更新多个状态

    区别就是使同步不可中断更新变成了异步可中断更新
    useDeferredValue和startTransition用来标记一次非紧急更新

二、React的设计思想

  • 组件化

每个组件都符合开放-封闭原则,封闭是针对渲染工作流来说的,指的是组件内部的状态都由自身维护,只处理内部的渲染逻辑。开放是针对组件通信来说的,指的是不同组件可以通过props(单项数据流)进行数据交互

  • 数据驱动视图

UI=f(data)
通过上面这个公式得出,如果要渲染界面,不应该直接操作DOM,而是通过修改数据(state或prop),数据驱动视图更新

  • 虚拟DOM

由浏览器的渲染流水线可知,DOM操作是一个昂贵的操作,很耗性能,因此产生了虚拟DOM。虚拟DOM是对真实DOM的映射,React通过新旧虚拟DOM对比,得到需要更新的部分,实现数据的增量更新

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

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

JSX与JS的区别:

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

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

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

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

jsx通过babel转义时,调用了React.createElement函数,它接收三个参数,分别是type元素类型,props元素属性,children子元素。

如下图所示,从jsx到真实DOM需要经历jsx->虚拟DOM->真实DOM。如果组件首字母为小写,它会被当成字符串进行传递,在创建虚拟DOM的时候,就会把它当成一个html标签,而html没有app这个标签,就会报错。

组件首字母为大写,它会当成一个变量进行传递,React知道它是个自定义组件就不会报错了

<app>lyllovelemon</app>
// 转义后
React.createElement("app",null,"lyllovelemon")

<App>lyllovelemon</App>
// 转义后
React.createElement(App,null,lyllovelemon)

React组件为什么不能返回多个元素

这个问题也可以理解为React组件为什么只能有一个根元素,原因:

  1. React组件最后会编译为render函数,函数的返回值只能是1个,如果不用单独的根节点包裹,就会并列返回多个值,这在js中是不允许的

    class App extends React.Component{
      render(){ 
        return(
        <div>
         <h1 className="title">lyllovelemon</h1>
          <span>内容</span>	
        </div>	
      )
    }
    
    //编译后
    class App extends React.Component{
      render(){
        return React.createElement('div',null,[
          React.createElement('h1',{className:'title'},'lyllovelemon'),
          React.createElement('span'),null,'内容'
          ])
      }
    }
    
  2. react的虚拟DOM是一个树状结构,树的根节点只能是1个,如果有多个根节点,无法确认是在哪棵树上进行更新

    vue的根节点为什么只有一个也是同样的原因
    React组件怎样可以返回多个组件

    使用HOC(高阶函数)
    使用React.Fragment,可以让你将元素列表加到一个分组中,而且不会创建额外的节点(类似vue的template)

    renderList(){
      this.state.list.map((item,key)=>{
        return (<React.Fragment>
          <tr key={item.id}>
            <td>{item.name}</td>
            <td>{item.age}</td>
            <td>{item.address}</td>
          </tr>	
        </React.Fragment>)
      })
    }
    
  3. 使用数组返回

    renderList(){
      this.state.list.map((item,key)=>{
        return [
          <tr key={item.id}>
            <td>{item.name}</td>
            <td>{item.age}</td>
            <td>{item.address}</td>
          </tr>
        ]
      })
    }
    

React中元素和组件的区别

react组件有类组件、函数组件

react元素是通过jsx创建的

const element = <div className="element">我是元素</div>	

四、简述React的生命周期

生命周期指的是组件实例从创建到销毁的流程,函数组件没有生命周期,只有类组件才有,因为只有class组件会创建组件实例
组件的生命周期可以分为挂载、更新、卸载阶段

  • 挂载

    constructor 可以进行state和props的初始化

    static getDerivedStateFromProps

    render

    componentDidMount 第一次渲染后调用,可以访问DOM,进行异步请求和定时器、消息订阅

  • 更新

    当组件的props或state变化会触发更新

    static getDerivedStateFromProps

    shouldComponentUpdate 返回一个布尔值,默认返回true,可以通过这个生命周期钩子进行性能优化,确认不需要更新组件时调用

    render

    getSnapShotBeforeUpdate

    componentDidUpdate 在组件完成更新后调用

  • 卸载

    componentWillUnmount 组件从DOM中被移除的时候调用

  • 错误捕获

    static getDerivedStateFromError 在errorBoundary中使用

    componentDidCatch

    render是class组件中唯一必须实现的方法

五、React事件机制

什么是合成事件

React基于浏览器的事件机制实现了一套自身的事件机制,它符合W3C规范,包括事件触发、事件冒泡、事件捕获、事件合成和事件派发等

React事件的设计动机(作用):

  • 在底层磨平不同浏览器的差异,React实现了统一的事件机制,我们不再需要处理浏览器事件机制方面的兼容问题,在上层面向开发者暴露稳定、统一的、与原生事件相同的事件接口

  • React把握了事件机制的主动权,实现了对所有事件的中心化管控

  • React引入事件池避免垃圾回收,在事件池中获取或释放事件对象,避免频繁的创建和销毁

React事件机制和原生DOM事件流有什么区别

虽然合成事件不是原生DOM事件,但它包含了原生DOM事件的引用,可以通过e.nativeEvent访问

DOM事件流是怎么工作的,一个页面往往会绑定多个事件,页面接收事件的顺序叫事件流

W3C标准事件的传播过程:

  1. 事件捕获
  2. 处于目标
  3. 事件冒泡

常用的事件处理性能优化手段:事件委托

把多个子元素同一类型的监听函数合并到父元素上,通过一个函数监听的行为叫事件委托

我们写的React事件是绑定在DOM上吗,如果不是绑定在哪里

React16的事件绑定在document上, React17以后事件绑定在container上,ReactDOM.render(app,container)

React事件机制总结如下:

事件绑定 事件触发

  • React所有的事件绑定在container上(react17以后),而不是绑定在DOM元素上(作用:减少内存开销,所有的事件处理都在container上,其他节点没有绑定事件)

  • React自身实现了一套冒泡机制,不能通过return false阻止冒泡

  • React通过SytheticEvent实现了事件合成
    在这里插入图片描述
    React实现事件绑定的过程

    建立合成事件与原生事件的对应关系

      registrationNameModule, 它建立了React事件到plugin的映射,它包含React支持的所有事件的类型,用于判断一个组件的prop是否是事件类型
      
    
      ```typescript
      {
         onBlur:SimpleEventPlugin,
         onClick:SimpleEventPlugin,
         onClickCapture:SimpleEventPlugin,
         onChange:ChangeEventPlugin,
         onChangeCapture:ChangeEventPlugin,
         onMouseEnter:EnterLeaveEventPlugin,
         onMouseLeave:EnterLeaveEventPlugin,
         ...  
      }
      ```
      **registrationNameDependencies**, 这个对象记录了React事件到原生事件的映射
      
    
      ```typescript
      {
        onBlur: ['blur'],
        onClick: ['click'],
        onClickCapture: ['click'],
        onChange: ['blur', 'change', 'click', 'focus', 'input', 'keydown', 'keyup', 'selectionchange'],
        onMouseEnter: ['mouseout', 'mouseover'],
        onMouseLeave: ['mouseout', 'mouseover'],
      }
      ```
    

    plugins对象, 记录了所有注册的插件列表

    plugins = [LegacySimpleEventPlugin, LegacyEnterLeaveEventPlugin, ...]
    

为什么针对同一个事件,即使可能存在多次回调,document(container)也只需要注册一次监听

因为React注册到document(container)上的并不是一个某个DOM节点具体的回调逻辑,而是一个统一的事件分发函数dispatchEvent - > 事件委托思想

dispatchEvent是怎么实现事件分发的

事件触发的本质是对dispatchEvent函数的调用
在这里插入图片描述

React事件处理为什么要手动绑定this

react组件会被编译为React.createElement,在createElement中,它的this丢失了,并不是由组件实例调用的,因此需要手动绑定this

为什么不能通过return false阻止事件的默认行为

因为React基于浏览器的事件机制实现了一套自己的事件机制,和原生DOM事件不同,它采用了事件委托的思想,通过dispatch统一分发事件处理函数

React怎么阻止事件冒泡

  • 阻止合成事件的冒泡用e.stopPropagation()
  • 阻止合成事件和最外层document事件冒泡,使用e.nativeEvent.stopImmediatePropogation()
  • 阻止合成事件和除了最外层document事件冒泡,通过判断e.target避免
document.body.addEventListener('click',e=>{
  if(e.target && e.target.matches('div.stop')){
    return
  }
  this.setState({active:false})
})

HOC和hooks的区别

useEffect和useLayoutEffect区别

React性能优化手段

  1. shouldComponentUpdate
  2. memo
  3. getDerviedStateFromProps
  4. 使用Fragment
  5. v-for使用正确的key
  6. 拆分尽可能小的可复用组件,ErrorBoundary
  7. 使用React.lazy和React.Suspense延迟加载不需要立马使用的组件

六、常用组件

错误边界

React部分组件的错误不应该导致整个应用崩溃,为了解决这个问题,React16引入了错误边界

使用方法:

React组件在内部定义了getDerivedStateFromError或者componentDidCatch,它就是一个错误边界。getDerviedStateFromError和componentDidCatch的区别是前者展示降级UI,后者记录具体的错误信息,它只能用于class组件

import React from "react"
class ErrorBoundary extends React.Component{
  constructor(props){
    super(props)
    this.state={
      hasError:false
    }
  }
  staic getDerivedStateFromError(){
    return { hasError:true}
  }
  componentDidCatch(err,info){
    console.error(err,info)
  }
  render(){
    if(this.state.hasError){
      return <div>Oops,err</div>
    }
    return this.props.children
  }
}

// App.jsx
import React from "react"
import ErrorBoundary from "./components/ErrorBoundary"
import ComponentA from "./components/ComponentA"
export class App extends React.Component{
  render(){
    return (
      <ErrorBoundary>
        <ComponentA></ComponentA>
      </ErrorBoundary>
    )
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文带你梳理React面试题(2023年版本) 的相关文章

随机推荐

  • 小程序使用 svg

    封装组件 components下新建svgIcon文件 wxml
  • node内置模块——Buffer模块(缓冲区)

    文章目录 Buffer 缓冲区 创建Buffer 利用字符串创建buffer Buffer from 使用Buffer方法创建buffer Buffer alloc Buffer allocUnsafe size Buffer元素的操作 B
  • CSS选择器(nth-child)

    nth child 这个选择符括号内可以写 an b a b均为整数 或者关键字 因为工作中有遇到要隐藏列表第三个子元素之后的所有子元素 所以有用到这个选择器 记录一下 1 nth child a 当括号里只写一个数字 比如 list li
  • OSI七层参考模型和数据封装

    OSI七层参考模型和数据封装 一 网络基础原理 1 分层思想 二 OSI七层参考模型 三 TCP IP协议簇和OSI参考模型 1 TCP IP五层模型常见协议 2 ARP是地址解析协议 3 RARP是反地址解析协议 四 PDU协议数据单元数
  • Flash概念简述

    1 Flash全名叫做Flash Memory Flash存储芯片的一种 通过特定的程序可以修改里面的数据 Flash属于非易失性存储设备 Non volatile Memory Device 与此相对应的是易失性存储设备 Volatile
  • RK3568 HDMI EDID处理过程

    一 简介 EDID是什么 EDID的全称是Extended Display Identification Data 扩展显示标识数据 VGA DVI的EDID由主块128字节组成 HDMI的EDID增加扩展块 128字节 扩展块的内容主要是
  • Jupyter notebook 如何设定默认的保存目录?

    前言 做智能车的时候 Jupter Notebook的默认保存在可怜的C盘 本来就很紧张的C肯定受不了 要改到别的地方 网上找了一些参考 说变更一下配置地址就可以了 照着做 99 的博客说 设置完了 关闭重启就好了 试了几次 根本不是关闭重
  • Proxmox VE 使用ACME 自动获取证书(DNSPOD)

    前言 PVE中自带了ACME 的支持 但是在国内对DNSPOD的支持似乎不是很好 所以只能采取手动的方式 一 使用步骤 以下步骤均在pve 的管理界面中 打开节点的shell 窗口中执行 安装ACME wget O acme1 sh htt
  • 页面访问量和网站访问量的统计

    网页点击计数器 以下是实现一个简单的基于 Servlet 生命周期的网页点击计数器需要采取的步骤 在 init 方法中初始化一个全局变量 每次调用 doGet 或 doPost 方法时 都增加全局变量 如果需要 您可以使用一个数据库表来存储
  • c++ 四元数转欧拉角

    c 四元数转欧拉角 两种方法 供你选择 方法 输入 x y z w 为四元数 输出 roll pitch yaw欧拉角 static void toEulerAngle const double x const double y const
  • FPGA驱动0.96oled显示屏 (4线 SPI) verilog语言

    之前也陆陆续续看了很多博客 也都能在自己的屏幕上显示出来 但是问题就是不知道怎么修改代码显示自己希望显示的东西 而且由于没注释原因看不太懂 最终的实现效果最终实现效果视频 b站视频链接1 评论区有人给了源码的百度网盘链接 csdn博客链接1
  • 1、若依服务网关

    文章目录 一 基础介绍 二 使用网关 1 添加依赖 2 resources application yml 配置文件 3 网关启动类 三 路由规则 1 Datetime 2 Cookie 3 Header 4 Host 5 Method 6
  • LeetCode Week 2

    LeetCode Week 2 保护好你的身体 和病痛比起来 其他的困难都不值一提 问题集合 1 Invert Binary Tree Easy 226 Invert a binary tree 4 2 7 1 3 6 9 to 4 7 2
  • Navicat远程连接MySQL服务器

    文章目录 一 准备 二 配置Navicat允许远程连接MySQL数据库 1 使用Navicat直接连接MySQL 2 使用 Navicat 通过 SSH 远程登录后再本地方式连接 MySQL 3 查看连接 为什么使用ssh登录 1 便捷性
  • AI工程师职业经验指南——转转推荐算法部负责人告诉你如何能够成为一名合格的机器学习算法工程师

    文章转载自 程序员杂志 2017 11 成为一名合格的开发工程师不是一件简单的事情 需要掌握从开发到调试到优化等一系列能力 这些能力中的每一项掌握起来都需要足够的努力和经验 而要成为一名合格的机器学习算法工程师 以下简称算法工程师 更是难上
  • 网络内安全试验场第三次CTF答题夺旗赛

    最近参加了网络内安全试验场第三次CTF答题夺旗赛 写wp 以后要做一个每次比赛完立马写wp的菜鸡 个人习惯 我做题一般喜欢从杂项 隐写开始 第一题 下载完成之后发现是个word 打开时需要密码 但是在题目中给提示了 所以直接输入密码 我以为
  • 自己手动搭建ssm框架实现增删改查、图片的上传、排序的移动所遇到问题的总结

    如图所示实现的增删改查 上移和下移 总结一下自己的不足之处 以前的公司都是自己有封装的框架而且有一段时间没做mvc了对此没有那么的熟悉 1 controller层返回的ModelAndView 后面希望能够改成String 然后再通过视图解
  • UTXO详解

    UTXO详解 https blog csdn net ztemt sw2 t 1 https blog csdn net yzpbright article details 82218759 比特币交易中的基础构建单元是交易输出 交易输出是
  • DBeaver连接clickhouse

    一 安装DBeaver 下载地址 Download DBeaver Community 1 选择自己电脑的安装包 2 安装完成之后 启动安装程序 3 选择语言及安装路径等 确认安装 4 安装成功 二 连接clickhouse 1 启动dbe
  • 一文带你梳理React面试题(2023年版本)

    前言 一 React18有哪些更新 setState自动批处理 在react17中 只有react事件会进行批处理 原生js事件 promise setTimeout setInterval不会 react18 将所有事件都进行批处理 即多