当前位置: 首页 > news >正文

前端面试题(React 与 Vue)

目录

一、React

函数组件

Fiber架构

组件重新渲染

组件通信

为什么不能在if中使用hook

useEffect与useLayoutEffect区别

性能优化hooks

受控组件与非受控组件

redux与zustand区别

二、Vue

vue2与vue3区别

生命周期

computed与watch区别

v-if与v-show区别

v-model如何实现

ref与reactive区别

组件通信

key的作用,为什么不建议用index作为key

虚拟dom与diff算法

响应式原理与proxy

对keep-alive的理解

vue与react中diff算法区别

vue与react区别


一、React

函数组件

函数式编程摒弃传统的面向对象思想,比起指令式编程,函数式编程更强调程序执行的结果,而不是过程。

一个简单的函数如何实现复杂的功能?
React将HTML、js全部写在函数组件中,函数组件的返回值就是要渲染的UI,这种设计更注重于js逻辑,而无需像vue一样提供各种模板语法。
还有就是状态,需要逻辑状态改变来触发页面的更新。函数组件是一个纯函数,通过函数自身的执行去渲染界面,本身无法存留状态,所以采用hooks的方式为组件提供状态,既不破坏纯函数的特性,又能在状态变化时让函数组件重新执行并更新页面。
还有生命周期,React函数组件摒弃了这个概念,取而代之的是使用useEffect在特定时候让函数组件重新执行并渲染。

React更愿意在函数上做文章,用函数作为组件的基础,用纯函数简单的重复执行来代替复杂的视图更新流程,Hooks也是函数,useState触发函数组件的执行,并作为纯函数执行的不同输入,useEffect将函数组件中的所有副作用从函数中隔离出去,自定义hooks也是函数。React看起来如此复杂的框架,归根结底只是通过函数的执行去渲染出视图而已,这也是React如此简洁和优雅的原因。

Fiber架构

Fiber 架构实现可中断的异步渲染,解决了传统同步渲染阻塞主线程的问题。

Fiber 节点结构
每个组件对应一个 Fiber 节点,构成链表树。节点包含组件类型、状态、副作用标记,节点指针等。

主要流程
状态更新触发组件重新渲染,启动更新流程。

Render 渲染阶段构建 Fiber 树
利用时间切片将渲染任务拆分为微任务,通过调度器按优先级异步执行,渲染过程可被高优先级任务中断。
从根节点开始采用深度优先遍历,递归处理每个 Fiber 节点,逐步构建 Fiber 链表树。
diff 算法对比新旧虚拟 DOM,复用 key 和类型相同的节点,标记新增 / 更新 / 删除等副作用,处理子节点时需要两次遍历,第一次遍历进行匹配和复用,第二次遍历进行增删和移动。
当节点没有子节点时,自底向上收集副作用,将子节点的副作用合并到父节点,最终形成从根节点到子节点的副作用链表。
每处理完一个 Fiber 节点,就检查时间片是否耗尽,如果耗尽就暂停任务并保存进度,等浏览器空闲时继续执行渲染任务。

Commit 提交阶段更新真实 DOM
遍历副作用链表,批量执行 DOM 操作,完成视图渲染。

构建完成的 Fiber 链表树替换当前的 Fiber 链表树,保证视图连续性。它们通过一个指针相关联,以便复用节点数据。

时间切片/调度器:设置任务优先级,将任务拆分为微任务,每次执行完微任务后,检查是否有高优先级任务插队执行,然后通过 MessageChannel 实现异步调度,利用浏览器空闲时间执行。

简要概述:

Render渲染阶段用时间切片将渲染任务拆分为微任务来执行,用深度优先遍历构建 Fiber 链表树,Diff 算法对比新旧 Fiber 节点,然后标记副作用,自底向上收集副作用形成副作用链表,每处理完一个 Fiber 节点就检查是否有高优先级任务,如果有就暂停渲染并保存进度,等浏览器空闲时继续执行渲染任务。

​​Commit提交阶段遍历副作用链表,批量执行 DOM 操作,完成视图渲染。

组件重新渲染

  • 状态更新
  • 父组件传递来的props变化
  • 强制更新ForceUpdate
  • 父组件重新渲染
  • 上下文Context变化

组件通信

  • 父传子props
  • 子传父回调函数
  • 跨层级context API
  • 复杂状态管理使用redux或zustand
  • 事件总线event bus可跨层级但破坏单向数据流
  • ref使用子组件

为什么不能在if中使用hook

React顶层调用规则,要求每次渲染时hooks的调用顺序必须完全一致。

React使用链表来管理hooks状态,每个hook根据它在链表中的位置来标识。每次渲染时,react会严格记录hooks的调用顺序,组件重新渲染时react会按上一次渲染时hooks的执行顺序来匹配对应状态,如果使用if等语句,可能会导致hooks在链表中的顺序发生变化,使得react的无法关联正确的状态。

useEffect与useLayoutEffect区别

useEffect 的设计是用来执行一段和当前渲染无关的副作用代码,同时解决了和生命周期相关的一些问题,通过依赖项数组的不同,实现不同的功能,使用return来清理副作用。
useLayoutEffect 执行需同步执行的逻辑,如DOM测量或修改。
useEffect 浏览器绘制后​​异步执行,不阻塞渲染​;useLayoutEffect:浏览器绘制前同步执行,阻塞渲染​

性能优化hooks

useCallback 与 useMemo 缓存
useCallback 缓存一个方法,useMemo 缓存一个计算后的值,当它们的依赖无变化时就不会重新声明或计算
因为函数组件通过重复执行的方式不停的执行自身渲染视图,在函数组件内部声明的方法每次都会被重新声明,函数组件中写的逻辑每次都会被重新执行,可能会带来一定的浪费,极端情况下会影响页面性能。
React.memo
防止子组件不必要的渲染,当父组件重新渲染时,如果父组件传递给子组件的props没有变化,则子组件不会重新渲染。
但这并不意味着需要滥用这些性能优化方法,应当在真的存在问题的时候再去考虑用它解决。比如真的在函数组件里写了一个cpu计算超级复杂的逻辑,频繁执行确实会影响组件的渲染,此时再考虑用 useMemo 去缓存计算结果。

受控组件与非受控组件

React中的组件根据是否受React控制可分为受控组件和非受控组件。
受控组件的值由React组件的状态控制,由用户输入触发React状态更新,然后触发重新渲染并更新DOM;
非受控组件的值不受React控制,由自身管理状态,通常使用ref来获取输入值,由用户输入直接更新DOM。
一般推荐使用受控组件来实现表单,数据由React组件负责处理。

redux与zustand区别

Zustand提供了轻量级状态管理,API简洁易用;Redux 相比之下是重量级状态管理,适用于大型或复杂项目,使用起来没有Zustand简洁。

状态模型: 
在Zustand中直接通过设置set来更新状态; Redux则使用action和reducer来管理状态。

更新机制:
Redux 的更新机制会触发全局检查,依赖开发者用手动优化来避免无效渲染;而 Zustand 仅通知关联字段的组件更新,实现真正按需渲染。

语法差异:
Redux中是不可变数据,也就是当更新状态时不直接修改原数据​​;而Zustand是可变语法,允许直接修改状态属性​​,底层库自动处理不可变状态转换。


  

二、Vue

vue2与vue3区别

vue2是选项式API,vue3是组合式API,提供了set up语法糖,还有响应式API:Ref和reactive,解决了vue2选项式API的逻辑碎片化问题。
Vue2使用object.defineProperty来实现对数据对象属性的getter和setter进行拦截,从而实现双向数据绑定及响应式更新。
Vue3使用ES6中的proxy来代理整个数据对象,提供完整的响应式机制,能更好的处理数组变化并监听深层对象属性变化。
生命周期钩子做了调整,Vue3中的模板支持多个根节点,且更好的支持ts。

生命周期

Vue2选项式API生命周期钩子:
beforeCreate → created → beforeMount → mounted → beforeUpdate → updated → beforeUnmount → unmounted
Vue3组合式API在 setup() 中使用生命周期钩子,前缀为 on,beforeCreate 和 created 被 setup() 替代。

setup是组合式API的入口,用于定义响应式数据、方法和生命周期钩子,setup中没有this。

computed与watch区别

computed的用于派生数据,也就是依赖其他值计算的结果,且具有缓存机制,只有依赖值变化,才会重新计算,如果没有变化则直接返回缓存的值;而且计算属性本身就是响应式,改变会自动触发依赖更新。
watch一般用于监听特定的数据变化并执行副作用,比如异步请求或操作DOM等,支持深度监听和立即执行,需要显式监听。
WatchEffect可以自动追踪依赖,响应式数据变化时自动重新执行。

副作用
副作用是指在代码执行过程中,对外部环境产生的可观察变化,比如修改全局变量、操作DOM、发起网络请求、定时器操作、打印日志等。
纯函数:相同的输入必有相同的输出。

v-if与v-show区别

v-if条件渲染,元素不存在于DOM中,切换时触发组件的创建或销毁,适用于条件不经常变化或者很少改变的场景。
v-show通过CSS来控制隐藏和显示,速始终存在于DOM中,适用于需要频繁切换显示状态的场景。

v-if切换成本高,v-show初始渲染成本高。

v-model如何实现

​​ v-model 的主要功能是在​​表单输入或​​自定义 Vue 组件​​上创建​​双向数据绑定​​,它同时负责数据的读取和数据的写入。
在原生表单元素上的实现原理:

<input v-model="username">

等效于以下写法:

<input :value="username" @input="username = $event.target.value">

在自定义组件上的实现原理:

<custom-input v-model="username">

等效于以下写法:

<custom-input  :model-value="username"  @update:model-value="username = $event">

子组件通过 props 接收 modelValue,触发 update:modelValue 事件,也可以用 emits 触发更新。
组件内部实现:

props: ['modelValue'],
emits: ['update:modelValue'],
methods: {handleInput(e) {this.$emit('update:modelValue', e.target.value)}
}

ref与reactive区别

ref可以用于基本数据类型,也可以用于引用类型,而reactive只能用于对象。
使用ref需要加value,如果将ref用于对象,则会自动转化为reactive包装的。
如果直接替换一整个reactive对象,会失去响应式;直接解构也会失去响应式,需要使用toRefs。

组件通信

  • props/自定义事件,v-model,父子间通信
  • provide/inject,跨层级通信
  • 事件总线,任意组件间通信
  • 使用pinia管理状态
  • 父组件使用子组件的ref
  • 路由参数,用于页面间传递数据
  • 插槽,普通插槽是父组件传数据给子组件,作用域插槽是子组件数据传递给父组件
  • 浏览器本地存储

对keep-alive的理解

keep-alive用于缓存组件实例,避免重复销毁和渲染。
被包裹的组件会触发特殊生命周期,切换时执行 deactivated 钩子,不会被销毁,再次激活时执行 activated。使用LRU算法(最近最少使用)自动清理缓存。
​​可以指定缓存规则和最大缓存实例数。

key的作用,为什么不建议用index作为key

key的作用是帮助diff算法识别节点的身份,通过复用相同key的节点,来优化渲染性能。
如果用index作为key,如果列表中的某项被删除,则后面的元素index全都会改变,可能导致原本可以复用的节点被重新创建。
如果列表项有输入框内容,用index会导致状态错乱。
正确做法是使用能够唯一标识的值作为key,比如数据库ID。

虚拟DOM与diff算法

虚拟DOM是什么?
虚拟DOM是真实DOM的轻量级表示,它通过js的对象数来描述真实DOM的结构和属性。

为什么要有虚拟DOM?
因为直接操作DOM的代价比较高,涉及到浏览器的回流和重绘,频繁操作会有性能问题。因此虚拟DOM作为缓冲层,通过diff算法对比新旧虚拟DOM树,找到最小化的更新操作,批量更新真实DOM,减少直接操作次数。

局限性:内存开销,简单场景可能性能不足。

Diff算法用来对比新旧虚拟DOM树的差异,找到最小化的更新操作,应用到真实DOM。
Diff算法仅比较同一层级的节点,不跨层级对比,若节点类型不同,则销毁旧子树并创建新子树。如果类型相同,则会对比并更新属性,然后递归处理子节点。
在列表渲染中,通过key标识节点身份,如果没有key就会按索引顺序对比。

vue与react中diff算法区别

列表对比策略与节点移动优化不同
Vue3采用双端对比+LIS算法优化,React采用单向递归+key机制。

Vue3从列表头尾同步开始对比,预处理相同节点,对于剩下的乱序节点,通过最长递归子序列算法计算最小移动路径。
React采用两轮遍历,首轮按索引顺序更新可复用节点,次轮处理剩余节点的新增、删除或移动,因为fiber节点为单向链表。

响应式原理与proxy

vue3的响应式原理基于js的proxy对象来实现。
当使用reactive包装一个普通对象时,vue会创建一个代理对象Proxy,该对象会拦截所有对原始对象的访问和修改。
当访问对象的属性时,Proxy的get拦截器会被触发,然后执行依赖收集,将当前正在执行的代码,也就是副作用函数与该属性建立联系。
如果访问的是对象,vue会递归的将其转换为响应式对象。
当修改对象的属性时,Proxy的set拦截器会被触发,vue会检查新值与原值是否相同,如果不相同vue会触发更新,通知所有依赖该属性的副作用函数重新执行。
只有实际被使用的属性才会建立依赖。

ref将基本值包装到一个对象中,这个对象有一个.value属性。
访问.value的值时会触发getter,进行依赖收集;修改.value的值时会触发setter,检查值是否变化然后触发更新。

vue与react区别

相同点:都采用组件化,都使用虚拟DOM和diff算法,都实现数据驱动视图。都有父子组件传参数据状态管理和路由等。

核心理念不同
vue的核心理念是降低前端开发门槛,是一个灵活易用的渐进式框架,提供双向数据绑定。
React的核心思想是声明是声明式编程和单向数据流。

组件化差异
Vue将HTML CSS和js写在同一个文件中,就是.vue文件,使用的是组合式API。
React则把HTML和CSS全部写进js中,使用.jsx文件,使用的是函数组件。

Diff算法不同
Vue使用双端对比以及LIS算法,而react是单向递归与fiber架构。
Vue内置了很多黑魔法,而react则是社区生态丰富。
Vue上手简单,React更灵活。


http://www.dtcms.com/a/282607.html

相关文章:

  • #Datawhale组队学习#7月-强化学习Task1
  • Java大厂面试实录:从电商场景到AI应用的深度技术考察
  • 如何通过域名白名单​OVP防盗链加密视频?
  • 【游戏引擎之路】登神长阶(十八):3天制作Galgame引擎《Galplayer》——无敌之道心
  • AJ Security:实用的 Java Web 安全库
  • Qt获取hid设备信息
  • 抖音小程序开发如何避坑?如何接口调用与性能优化?
  • Nacos 开源 MCP Router,加速 MCP 私有化部署
  • 运维打铁: Ruby 脚本在运维自动化中的应用探索
  • leetcode 3201. 找出有效子序列的最大长度 I 中等
  • K8s 自定义调度器 Part1:通过 Scheduler Extender 实现自定义调度逻辑
  • AI Agent开发学习系列 - langchain之LCEL(1):LangChain LCEL链式编排与RAG增强实践
  • FunASR Paraformer-zh:高效中文端到端语音识别方案全解
  • Django ORM 查询工具对象详解
  • Linux 命令:uname
  • Scalefusion 与 EasyControl 对比:轻量级方案与全功能 IoT MDM 的深度碰撞
  • 前端面试题(HTML、CSS、JavaScript)
  • 在此处打开 powershell 窗口 shift + 右键 修改为 右键弹出
  • [硬件电路-28]:从简单到复杂:宇宙、芯片与虚拟世界的共通逻辑2
  • 1软件工程概念及其基本要素-思考题
  • Netty介绍和基本代码演示
  • 基于渐进式迁移学习网络(PTLN)​的小样本故障诊断模型
  • STM32CubeMX红外收发模块(串口)
  • H3CNE综合实验之五角星
  • 了解linux用户和权限
  • Linux开发利器:探秘开源,构建高效——基础开发工具指南(上)【包管理器/Vim】
  • springboot项目部署到K8S
  • Golang 并发快速上手
  • SingLoRA:单矩阵架构减半参数量,让大模型微调更稳定高效
  • ​HAProxy负载均衡集群概述