React高频面试题

本文全面介绍了React,包括其特性、生命周期、事件机制等。阐述了React的设计思想,如组件化、数据驱动视图等。还讲解了Fiber架构、虚拟DOM和diff算法,以及组件通信、状态管理等内容。同时对比了Vue和React的异同,为开发者提供了深入了解React的参考。

什么是 React

React用于动态构建用户界面的 JS UI库。

React特点

  • 使用虚拟DOM和Diff算法,尽量复用DOM节点,减少与真实DOM的交互
  • 使用 JSX,代码的可读性更好
  • 组件化模式,提高代码复用率、且让代码更好维护
  • 声明式编程, 让编码人员无需直接操作DOM,提高开发效率

React声明式编程:我们只声明了我们想要的内容:比如一个包含用户姓名的无序列表,而不用关心具体的DOM操作。React会负责渲染和更新DOM,我们只需关注描述界面的声明式代码。

React的设计思想

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

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

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

React生命周期

React的生命周期:React实例从被创建到被销毁的过程,React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。

只有 class 组件才有生命周期,因为 class 组件会创建对应的实例,而函数组件不会 。

React 生命周期主要包括三个阶段:挂载阶段,更新阶段,卸载阶段
在这里插入图片描述

class Test extends React.Component{
   
   
    constructor(props){
   
    // 构造函数
        super(props)
        this.state = {
   
   }
    }
    // 初始化,更新时会调用
    static getDerivedStateFromProps(props,state){
   
   
        // 必须返回一个对象,会和state合并
        return {
   
   }
    }
    // 初始化渲染时使用
    componentDidMount(){
   
   }
    // 组件更新时调用 返回false 不更新
    shoudComponentUpdate(prevProps,nextState){
   
    return true }
    // 组件更新时调用,返回的值会设置在componentDidUpdate的第三个参数
    getSnapshotBeforeUpdate(prevprops,nextState) {
   
    return ''}
    // 组件更新后调用
    componentDidUpdate(preProps,preState,valueFromSnaspshot){
   
   }
    // 组件卸载时调用
    componentWillUnmount() {
   
   }
    // 组件抛出错误
    static getDerivedStateFromError(){
   
   }
}

挂载阶段

挂载阶段:组件实例被创建和插入 DOM 树的过程

  • constructor 初始化阶段,可以进行state和props的初始化
  • static getDerivedStateFromProps 静态方法,不能获取this
  • render 创建虚拟DOM的阶段
    componentDidMount 第一次渲染后调用,挂载到页面生成真实DOM,可以访问DOM,进行异步请求和定时器、消息订阅

更新阶段

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

  • shouldComponentUpdate 返回一个布尔值,默认返回true,可以通过这个生命周期钩子进行性能优化,确认不需要更新组件时调用
  • render 更新虚拟DOM
  • getSnapShotBeforeUpdate 获取更新前的状态
  • componentDidUpdate 在组件完成更新后调用,更新挂载后生成真实DOM
    当组件的props或state变化会触发更新

卸载阶段

  • componentWillUnmount 组件从DOM中被移除的时候调用,通常在这个阶段清除副作用,比如定时器、事件监听等

错误捕获

  • static getDerivedStateFromError 在errorBoundary中使用
  • render是class组件中唯一必须实现的方法

重要的勾子

  • render:初始化渲染或更新渲染调用。
  • componentDidMount:在组件挂载成功之后调用,该过程组件已经成功挂载到了真实 DOM 上。一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  • componentWillUnmount:在组件卸载成功之前调用,做一些收尾工作, 如: 清理定时器、取消订阅消息

已经废弃的勾子

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

React事件机制

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

在这里插入图片描述
在这里插入图片描述

React事件和普通的HTML事件有什么不同?

  • 对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰camelCase;
  • 对于事件函数处理语法,原生事件为字符串,react 事件为函数;
<button onclick="alert('Button clicked')">Click me</button>
function MyComponent() {
   
   
  return (
    <button onClick={
   
   ()=>{
   
   alert('Button clicked')}>Click me</button>
  );
}
  • react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。

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

React怎么阻止事件冒泡

  • 阻止合成事件的冒泡用e.stopPropagation()
  • 阻止合成事件和最外层document事件冒泡,使用e.nativeEvent.stopImmediatePropogation()
  • 阻止合成事件和除了最外层document事件冒泡,通过判断e.target避免

React事件是 react 模拟原生 DOM 事件所有能力的一个事件对象,其优点如下:

  • 兼容所有浏览器,更好的跨平台;
  • 将事件统一存放在一个数组,避免频繁的新增与删除(垃圾回收)。
  • 方便 react 统一管理和事务机制。

事件的执行顺序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到document 上合成事件才会执行。

JSX

JSX 是 JavaScript 语法扩展,可以在JS中编写XML的语言。它不能被浏览器直接识别,需要通过webpack、babel之类的编译工具转换为JS执行。
JSX是React.createElement的语法糖,使用JSX等价于React.createElement,React.createElement将返回一个ReactElement的js对象,编译工作交由babel操作
JSX 和 React 是相互独立的东西。JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。

import React from 'react';
//JSX不是字符串, 也不是HTML/XML标签,最终产生的就是一个JS对象
var ele = <h1>Hello, JSX!</h1> 
// 等价于
var element = React.createElement(
  "h1",
  null,
  "Hello, world!"
);

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

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

为什么React自定义组件首字母要大写?
从jsx到真实DOM需要经历jsx->虚拟DOM->真实DOM。如果组件首字母为小写,它会被当成字符串进行传递,在创建虚拟DOM的时候,就会把它当成一个html标签,而html没有app这个标签,就会报错。组件首字母为大写,它会当成一个变量进行传递,React知道它是个自定义组件就不会报错了

React组件为什么只能有一个根元素?(React组件为什么不能返回多个元素)
React的虚拟DOM是一个树状结构,树的根节点只能是1个,如果有多个根节点,无法确认是在哪棵树上进行更新

state 和 props有什么区别?

  • props 是传递给组件的(类似于函数的形参),是父组件向子组件传递数据的方式,state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量), 代表了随时间会产生变化的数据,应当仅在实现交互时使用

  • props 在组件内部是不可修改的,但 state 在组件内部可以进行修改

  • 组件可以选择把它的 state 作为 props 向下传递到它的子组件中

组件通信

单层级通信

  • 父组件给子组件通信,传递props
  • 子组件向父组件通信,使用回调函数(父组件向子组件传递一个函数,子组件在适当时机调用并传入自身的值或函数,这样父组件就可以拿到子组件传过来的参数了)
    还可以借助forwardRef 和 useImperativeHandle 实现父组件直接访问并操作子组件内部某些特定DOM元素或者自定义的方法。具体来说,forwardRef 用于将 ref 传递给函数组件,而 useImperativeHandle 则是用来定制函数组件暴露给父组件的实例方法或属性。

下面是一个简单的示例,展示如何使用 forwardRef 和 useImperativeHandle:

import React, {
   
    forwardRef, useImperativeHandle, useRef } from 'react';

// 定义一个子组件,它接收父组件传递过来的ref
const ChildComponent = forwardRef((props, ref) => {
   
   
  // 使用useRef创建一个内部引用,例如用于存储一个DOM元素
  const internalRef = useRef();

  // 在useImperativeHandle中定义子组件希望暴露给父组件的方法或属性
  useImperativeHandle
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值