# react进阶面试题
# redux与vuex的区别?
vuex 的流向:
view——>commit——>mutations——>state变化——>view变化
(同步操作)
view——>dispatch——>actions——>mutations——>state变化——>view变化
(异步操作)
redux 的流向:
view——>dispatch——>actions——>reducer——>state变化——>view变化
(同步异步一样)
不同点:
vuex 以 mutations 函数取代 redux 中的 reducer,只需在对应的 mutation 函数里改变 state 即可。
vuex 支中的 state 直接关联到组件实例上,当 state 变化时自动重新渲染,无需订阅重新渲染函数。redux 使用 store 对象存储整个应用的状态,状态变化时,从最顶层向下传递,每一级都会进行状态比较,从而达到更新。
vuex 支持 action 异步处理,redux 中只支持同步处理,对于异步处理需要借助于
redux-thunk
和redux-saga
实现。
# 能简单介绍一下 react 执行过程吗
jsx 经过 babel 转变成 render 函数 create update enqueueUpdate scheduleWork 更新 expiration time request Workwork Loop大循环: - performUnitOfWork - beginWork - completeUnitOfWork Effect Listcommit
# react性能优化
这里只说 react 单独的进行的性能优化:
- key
- shouldComponentUpdate
- pureComponent
- 关于箭头函数,先声明好事件监听函数后,然后再拿到其引用传给组件:
- useCallback(大计算量的函数来)
- useMemo
- React.Memo
- 不可变数据Immutable
- reselect
- React.lazy 按需加载
如果一定要做性能优化,核心还是在减少频繁计算和渲染上,在实现策略上主要有三种方式:利用key维持组件结构稳定性、优化数据比对过程和按需加载。其中优化数据比对过程可以根据具体使用的场景,分别使用缓存数据或组件、改用Immutable不可变数据等方式进行。最后,也一定记得要采用测试工具进行前后性能对比,来保障优化工作的有效性。
React性能优化小贴士 (opens new window)
# react的事件机制(合成事件)
React 的事件机制是通过合成事件来处理事件,提供了一致的跨浏览器事件接口,并使用事件委托来管理事件监听器。这使得事件处理在React组件中变得简单、可靠和高效。
React的合成事件是一种在原生浏览器事件基础上封装的事件系统。它提供了一致的跨浏览器事件接口,并解决了一些浏览器兼容性问题。下面是一些关键概念和特点:
合成事件对象(SyntheticEvent):React封装了一个合成事件对象,作为事件处理函数的参数。合成事件对象包含有关事件的信息,例如事件类型、目标元素、鼠标位置等。通过访问合成事件对象的属性和方法,可以获取和操作事件相关的数据。
事件委托(Event Delegation):React使用事件委托的方式来处理事件。它将事件监听器添加到组件的根元素上,然后通过事件冒泡机制来处理具体的事件。这样可以减少事件监听器的数量,提高性能和内存利用效率。
自动绑定(Automatic Binding):React自动将事件处理函数中的this关键字绑定到组件实例上。这意味着在事件处理函数中可以直接访问组件的属性和方法,无需手动绑定this或使用箭头函数。
事件池(Event Pooling):为了提高性能,React使用了事件池来重用合成事件对象。当事件处理函数执行完成后,合成事件对象将被重置并返回到事件池中,以便下次使用。这样可以减少内存分配和垃圾回收的开销。
异步事件处理(Asynchronous Event Handling):由于合成事件对象的重用,事件处理函数可能在异步环境中被调用。这意味着在事件处理函数中访问合成事件对象的属性时,应该立即提取需要的数据,而不是在稍后的时间点再访问。
React的合成事件机制使得事件处理在组件中变得简单、可靠和高效。它提供了一致的跨浏览器事件接口,并解决了一些常见的浏览器兼容性问题。同时,通过事件委托和自动绑定,React还提供了性能优化的机制,以提高事件处理的效率。
# 为什么虚拟dom比真实dom快
虚拟 DOM(Virtual DOM)相对于真实 DOM 具有一些优势,使其在某些情况下可以更快地更新和渲染页面。以下是一些原因:
批量&异步更新:虚拟 DOM 可以对多个 DOM 更新进行批量处理,异步更新。当应用程序状态发生变化时,虚拟 DOM 可以收集所有的变更,然后一次性更新真实 DOM。相比之下,直接操作真实 DOM 时,每次更新都会立即触发浏览器的重新渲染,这可能导致性能问题。
部分更新(节点操作的最小化):虚拟DOM可以通过比较前后两个虚拟DOM树的差异,找出需要更新的节点,而不是直接操作每个具体的真实DOM节点。这样可以避免不必要的节点操作,仅更新需要变化的部分,减少了不必要的计算和操作。而在直接操作真实 DOM 时,通常需要手动处理每个变化,这可能更加繁琐和耗时。
虚拟 DOM 的内存操作:虚拟 DOM 是在内存中操作的,而真实 DOM 是浏览器中的实际对象。内存中的操作比浏览器中的操作更快速。虚拟 DOM 可以在内存中进行计算和比较,然后再将最终结果应用到真实 DOM,这样可以减少对浏览器的操作次数。
批量样式计算:虚拟 DOM 可以对样式计算进行批量处理。在直接操作真实 DOM 时,每次更改样式都会触发浏览器的重新计算和重新布局,这可能会导致性能下降。虚拟 DOM 可以将多个样式变更收集起来,并一次性应用到真实 DOM,从而减少了不必要的计算和布局操作(减少重绘或回流)。
更新预测:虚拟 DOM 可以智能地分层更新,先更新可能依赖的部分,再更新后续部分。优化渲染顺序。
跨平台:虚拟 DOM 是 JavaScript 对象,无需考虑浏览器兼容性,运行效率高。真实 DOM 操作需要抹平各种平台差异。
需要注意的是,虚拟 DOM 也有一些开销。虚拟 DOM 需要在内存中维护一个额外的数据结构,并进行比较和计算,这可能会增加一些额外的 CPU 和内存消耗。在一些简单的应用程序中,直接操作真实 DOM 可能更加高效。但对于大型和复杂的应用程序,虚拟 DOM 在维护和管理状态变化方面提供了更好的可扩展性和性能优势。
总结起来,虚拟 DOM 通过批量更新、部分更新、内存操作和批量样式计算等优势,可以提高DOM操作的性能。然而,性能的提升效果也受到应用程序的规模和复杂度等因素的影响。
# React 中 useeffect 和 uselayouteffect 区别
在 React 中,useEffect
和 useLayoutEffect
都是用于处理组件的副作用(side effects)的钩子函数,它们之间的主要区别在于触发时机和执行顺序。
useEffect
:useEffect
是在组件渲染完成后异步执行的钩子函数。- 它不会阻塞组件的渲染过程,即它会在浏览器完成绘制后执行。
- 通常用于处理需要异步执行的副作用,比如数据获取、订阅事件、定时器等。
useEffect
的回调函数在组件每次渲染时都会执行,除非指定依赖项(dependency),否则会在每次渲染后都触发。
useLayoutEffect
:useLayoutEffect
是在组件渲染完成后同步执行的钩子函数。- 它会在浏览器执行绘制之前执行,即在浏览器更新屏幕之前执行。
- 通常用于处理需要同步执行的副作用,比如 DOM 操作、测量元素尺寸等。
useLayoutEffect
的回调函数在组件每次渲染时都会执行,类似于useEffect
,但它会在浏览器更新屏幕之前执行。
总结区别:
useEffect
是在浏览器完成绘制后异步执行,而useLayoutEffect
是在浏览器更新屏幕之前同步执行。useEffect
不会阻塞组件的渲染过程,而useLayoutEffect
可能会导致阻塞,因为它会在浏览器更新屏幕之前执行。- 一般情况下,应优先使用
useEffect
,除非需要在浏览器更新屏幕之前同步执行一些操作,才考虑使用useLayoutEffect
。
需要注意的是,过度使用 useLayoutEffect
可能会导致性能问题,因为它会阻塞组件的渲染过程。因此,在大多数情况下,推荐使用 useEffect
来处理副作用。只有在需要确保某些操作在浏览器更新屏幕之前同步执行时,才使用 useLayoutEffect
。
# react的 usememo 原理
useMemo 是 React 中的一个钩子函数,用于在组件渲染过程中进行性能优化,避免不必要的计算开销。它的原理是基于记忆化(memoization)的技术。
当使用 useMemo 时,你可以传入一个计算函数和一个依赖数组。React 会执行计算函数,并将其返回值缓存起来。在后续的渲染中,如果依赖数组中的值没有发生变化,React 将直接返回缓存的值,而不会重新执行计算函数。
useMemo 的原理可以简单概括为以下几个步骤:
在组件渲染过程中,遇到 useMemo,将计算函数和依赖数组传入。 React 检查依赖数组中的值是否发生变化。 如果依赖数组中的值没有发生变化,React 返回上一次缓存的值,跳过计算函数的执行。 如果依赖数组中的值发生变化,React 执行计算函数,并将计算结果缓存起来。 在下一次渲染过程中,重复上述步骤。 通过使用 useMemo,可以避免在每次渲染时都执行昂贵的计算操作,只有在依赖项发生变化时才重新计算。这可以显著提高组件的性能,特别是在处理大量数据或复杂计算的情况下。
需要注意的是,useMemo 的缓存值是在组件内部存储的,对于父组件的更新不会影响 useMemo 的缓存。每个组件都有自己独立的 useMemo 缓存。
另外,需要谨慎使用 useMemo,因为过度使用可能会导致代码复杂性增加。只有在确实需要进行性能优化时,才应该使用 useMemo。在大多数情况下,组件的正常渲染和更新机制已经足够高效,不需要额外的优化手段。
# react的 setstate 过程
在 React 中,setState 是用于更新组件状态的方法。当调用 setState 时,React 会进行以下一般性的更新过程:
合并更新:React 将传入的更新对象(可以是一个新的状态值或一个更新函数)与当前状态进行合并。它不会立即执行更新,而是将更新放入待处理的更新队列中。
批量更新:React 会将多个连续的 setState 调用合并为一个批量更新操作,以提高性能。在批量更新期间,React 不会立即更新组件,而是将所有更新推迟到后续的阶段。
准备更新:在进行实际更新之前,React 会执行一些准备工作。它会检查是否处于批量更新模式,以确定是否需要合并更新。如果在 React 事件处理程序、生命周期方法或异步代码中调用了 setState,React 会自动启动批量更新模式。
触发重新渲染:在准备更新阶段后,React 会触发组件的重新渲染过程。它会比较前后两次渲染的虚拟 DOM 树,找出需要更新的部分,并进行相应的 DOM 操作。
执行生命周期方法:在重新渲染过程中,React 会按照生命周期的顺序调用相应的方法,如 componentDidUpdate。
需要注意的是,由于 setState 是异步的,React 可能会对多个 setState 调用进行批量处理,以提高性能。这意味着连
# react diff 和 fiber 算法的区别是什么
React 的 Diff 算法和 Fiber 算法是 React 在不同版本中使用的两种不同的算法。
Diff 算法:
- Diff 算法是 React 早期版本使用的一种协调算法,也被称为 Reconciliation(协调)算法。
- Diff 算法的主要思想是通过比较前后两次渲染的虚拟 DOM 树,找出需要更新的部分,并进行相应的 DOM 操作。
- Diff 算法是一种递归算法,会遍历整个虚拟 DOM 树,进行全量比较,然后进行更新。
- Diff 算法的缺点是当组件层级较深或组件数量较多时,比较和更新的成本会较高,可能会导致性能问题。
Fiber 算法:
- Fiber 算法是 React 16 版本引入的一种新的协调算法,旨在提高 React 的渲染性能和用户响应度。
- Fiber 算法通过引入 Fiber 数据结构,将渲染过程分解为可中断的单元,使得 React 可以在渲染过程中优先处理高优先级的任务。
- Fiber 算法使用了一种增量渲染的方式,将渲染过程分为多个阶段,每个阶段可以根据优先级进行调度和中断。
- Fiber 算法通过优先级调度、时间切片和任务分片等技术,使得 React 能够更好地控制渲染过程,提高用户体验。
- Fiber 算法还支持并发模式,可以在多个线程上进行渲染,进一步提升性能。
- 错误处理:在Fiber中,一次渲染过程中的错误不会致命。React可以捕获错误,暂停一次更新,在不破坏整个应用的情况下显示错误信息。
总结: Diff 算法是 React 早期版本使用的一种全量比较的算法,而 Fiber 算法是 React 16 版本引入的一种增量渲染的算法。Fiber 算法通过引入 Fiber 数据结构和优先级调度等机制,提高了 React 的渲染性能和用户响应度。Fiber 算法还支持并发模式,可以在多个线程上进行渲染。相比之下,Fiber 算法在性能和用户体验方面有明显的优势。
# 什么场景下需要使用 immutable
Immutable 数据是指一旦创建就不能被修改的数据。在以下场景下,使用 Immutable 数据可以带来一些好处:
状态管理:在状态管理库(如 Redux,Redux要求State必须是immutable的,可以利用immutable优化性能。)中,使用 Immutable 数据可以确保状态的不可变性。这样可以避免状态被直接修改,提高状态的可预测性和可维护性。
函数式编程:Immutable 数据是函数式编程的重要概念之一。函数式编程鼓励使用纯函数和不可变数据,以避免副作用和共享状态。使用 Immutable 数据可以更轻松地实现函数式编程的思想。
性能优化:由于 Immutable 数据不可变,可以使用结构共享和持久化数据结构来优化性能。通过共享相同的数据结构,可以减少内存占用和提高数据操作的效率。
并发和多线程:在并发和多线程环境下,使用 Immutable 数据可以避免竞态条件和数据冲突。由于数据不可变,不需要担心数据被同时修改的问题,从而简化了并发编程的复杂性。
缓存和缓存一致性:Immutable 数据可以作为缓存键或缓存值使用。由于数据不可变,可以在缓存中安全地存储和共享,而不必担心数据被修改导致的缓存一致性问题。
需要注意的是,使用 Immutable 数据也会带来一些开销,因为每次更新数据都需要创建新的对象。在某些场景下,如果频繁地进行大量的数据更新操作,可能会导致性能下降。因此,在选择是否使用 Immutable 数据时,需要权衡其带来的好处和性能开销,并根据具体情况进行决策。