文章目录
- 前言
- 1.useEffect是异步还是同步
- 2.微任务和宏任务
- [3.事件循环(Event Loop)](#3.事件循环(Event Loop))
- 4.React性能优化手段
前言
准备了一些高频面试题,有需要的小伙伴可以收藏,需要的时候看看,也会持续更新。
1.useEffect是异步还是同步
useEffect的执行是异步的,这主要是为了确保它不会阻塞浏览器的绘制过程,从而保持用户界面的响应性。
为何是异步
1、避免阻塞浏览器绘制:
如果 useEffect
同步执行,那么在组件渲染过程中,所有的副作用逻辑(比如数据获取、DOM 操作、订阅等)都必须执行完毕,浏览器才能继续绘制。这会导致用户界面在副作用执行期间停顿,影响用户体验。
2、保持数据一致性:
React 的渲染过程是异步和批处理的。在渲染过程中,多个状态更新可能被批处理。如果 useEffect
是同步执行的,可能会导致在渲染过程中状态的不一致,因为副作用可能依赖于最新的渲染结果。异步执行 useEffect
确保了在所有 DOM 更新完成后再运行副作用逻辑,从而保证数据的一致性。
3、类比于生命周期:
在类组件中,副作用通常在 componentDidMount
和 componentDidUpdate
生命周期方法中执行。React 将这些方法的执行也设计为异步,以便优化性能和用户体验。useEffect
是函数组件中的等价 Hook,因此也遵循相同的异步设计原则。
4、更好的性能优化:
React 可以通过异步执行副作用来更好地管理性能。比如,React 可以在必要时跳过不需要的副作用,或在空闲时间(通过 requestIdleCallback
)执行一些较低优先级的副作用,从而提高应用的整体性能。
如何拿到最新的数据
1、使用useEffect依赖项:
useEffect
的第二个参数是一个数组,包含所有影响副作用执行的状态或变量。当这些依赖项发生变化时,useEffect
会重新执行。确保将数据作为依赖项传递给 useEffect
,这样可以保证在数据更新后,副作用会重新执行,从而拿到最新的数据。
2、使用 async/await:
如果你在 useEffect
中使用了异步函数(比如 fetch
),你可以使用 async/await 来确保在数据完全获取后再更新状态。
3、使用setTimeout:使用setTimeout
时,将延迟时间设置为0或者省略,可拿到需要的最新数据。
- 执行机制 :
setTimeout
和useEffect
在一起有多余的感觉,因为useEffect
本身就是异步,而setTimeout
也是异步的。即使没有setTimeout
,React 也会在组件渲染完成后立即执行,所以,setTimeout
并不会改变其执行时机。 - 性能考虑 :虽然将时间设置为 0 的
setTimeout
通常被用来将代码推迟到下一个事件循环中执行,但在这里,它只是增加了额外的无谓开销,没有提供任何实际的优势。这可能会对性能产生微小的负面影响。
2.微任务和宏任务
概念
1、微任务(microtask):
微任务是指在当前任务执行完毕后立即执行的任务。Promise回调函数、async/await(返回的也是一个promise)、process.nextTick、等都是微任务的例子。在React中,更新状态和执行副作用(如生命周期方法、钩子函数、effect hook等)通常被视为微任务。这意味着,当React组件的状态更新时,React会将相应的更新计划为微任务,以确保它们在当前任务执行结束后立即执行。
2、宏任务(macrotask):
宏任务是指需要在事件循环的下一个迭代中执行的任务。定时器(setTimeout、setInterval)、I/O 操作、UI 渲染等通常被视为宏任务。在React中,调度新的组件渲染、处理用户输入等也被认为是宏任务。
优点
在React中,通过将任务分解为微任务和宏任务,可以实现异步更新 和优化性能。例如,React可以利用微任务将多个状态更新合并成一个,从而减少渲染次数,提高性能。同时,宏任务的使用也确保了React能够在用户交互等事件之后及时更新UI,提供良好的用户体验。
3.事件循环(Event Loop)
概念
事件循环(Event Loop)是Javascript运行时环境的一部分,它负责管理代码的执行顺序,简单来说就是Javascript的一种运行机制,解决浏览器的单线程问题。
Javascript单线程任务从时间上分为同步任务和异步任务,而异步任务又分为宏任务(macrotask)和微任务(microtask)。
执行机制
- 执行全局同步代码,将其中的函数调用推入调用栈中。
- 当调用栈为空时,事件循环开始执行微任务队列中的任务,直到微任务队列为空。
- 从任务队列中取出一个任务,并将其推入调用栈中执行。
- 重复上述步骤,直到任务队列和微任务队列都为空。
4.React性能优化手段
- 避免不必要的渲染 :合理使用
shouldComponentUpdate
和React.PureComponent
,使用shouldComponentUpdate
来控制组件是否需要重新渲染。React.PureComponent
自动实现了shouldComponentUpdate
,浅比较 props 和 state,函数组件可使用React.memo
。 - 使用合适的数据结构和状态管理 :使用不可变数据结构(如
Immutable.js
)有助于高效地进行数据比较和变更检测;适当地提升状态,避免在层级过深的组件树中传递大量数据。使用React.Context
来避免深层级的 props 传递。 - 避免匿名函数和对象:在渲染方法中避免定义匿名函数和对象,因为每次渲染都会生成新的引用,导致子组件的重新渲染。
- 代码分割和懒加载 :使用
React.lazy
和React.Suspense
进行代码分割和组件懒加载,减少初始加载时间。 - 使用虚拟化列表 :对于长列表,使用虚拟化技术(如
react-window
或react-virtualized
)来只渲染视口中的项目,避免渲染大量的 DOM 节点。 - 合理使用
useCallback
和useMemo
:对于函数和复杂计算结果,使用useCallback
和useMemo
来避免不必要的重新创建。 - 列表项使用 key 属性 :
key
属性帮助 React 高效地进行 DOM diff 算法,从而减少不必要的 DOM 操作,当列表发生变化时,React 可以使用key
来确定哪些元素需要更新、插入或删除。 - 使用生产模式:确保在生产环境中使用 React 的生产版本。开发版本包含额外的警告和检查,性能较低,生产版本会进行代码压缩和性能优化。