以下是一些React高级面试题:
一、组件相关
-
React组件的生命周期有哪些(类组件)?在函数组件中如何实现类似功能?
- 答案:
- 类组件生命周期:
componentDidMount
:组件挂载后调用,常用于数据获取(如fetch
请求)、添加事件监听等操作。componentDidUpdate
:组件更新后调用,可用于根据新的props
或state
进行操作,但要注意避免无限循环更新(例如在componentDidUpdate
中直接修改state
且没有合适的条件判断时会导致无限循环)。componentWillUnmount
:组件卸载前调用,用于清理工作,如清除定时器、取消网络请求、移除事件监听等。
- 函数组件中可以使用
useEffect
钩子来模拟类组件生命周期。useEffect
的依赖数组为空([]
)时,相当于componentDidMount
和componentWillUnmount
的组合;有依赖项时,每次依赖项变化时执行类似componentDidUpdate
的操作。
function MyComponent() { useEffect(() => { // 类似componentDidMount操作,如数据获取 return () => { // 类似componentWillUnmount操作,如清除定时器 }; }, []);// 空数组表示只在挂载和卸载时执行 }
- 类组件生命周期:
- 答案:
-
如何实现React组件的高阶组件(HOC)?有什么应用场景?
- 答案:
- 高阶组件是一个函数,它接受一个组件并返回一个新的组件。
function withLogger(WrappedComponent) { return class extends React.Component { componentDidMount() { console.log('Component has mounted:', WrappedComponent.name); } render() { return <WrappedComponent {...this.props} />; } }; }
- 应用场景包括代码复用(如权限验证、数据获取逻辑复用等)、对组件进行包装以添加额外的功能(如日志记录、性能统计等)。
- 答案:
二、状态管理相关
- 对比Redux和React Context API在状态管理方面的优缺点?
- 答案:
- Redux:
- 优点:
- 具有单一数据源,方便管理全局状态。
- 提供了丰富的中间件(如
redux - thunk
、redux - saga
等)来处理异步操作。 - 有成熟的生态系统,社区支持强大,有很多工具(如
redux - devtools
)可用于调试。
- 缺点:
- 配置相对复杂,对于小型应用可能会引入过多不必要的代码。
- 学习成本较高,需要理解
actions
、reducers
、store
等概念。
- 优点:
- React Context API:
- 优点:
- 简单易用,不需要额外安装库,在React内部就可以使用。
- 适用于简单的全局状态共享场景,不需要像Redux那样复杂的结构。
- 缺点:
- 当应用规模增大时,性能可能会受到影响,因为Context的更新会导致所有消费该Context的组件重新渲染。
- 缺乏像Redux那样成熟的中间件来处理复杂的异步操作等情况。
- 优点:
- Redux:
- 答案:
三、性能优化相关
- React的性能优化有哪些常见方法?
- 答案:
- 使用
React.memo
对函数组件进行浅比较优化,避免不必要的重新渲染。 - 在类组件中使用
shouldComponentUpdate
生命周期方法(在React 16.3+中推荐使用React.PureComponent
来简化这个过程)来手动控制组件是否更新。 - 合理使用
key
属性,在列表渲染时确保key
的唯一性且稳定,这有助于React识别哪些元素发生了变化,从而提高渲染效率。 - 对于大型组件树,可以采用懒加载(
React.lazy
结合Suspense
)来按需加载组件,减少初始加载时间。
- 使用
- 答案:
四、React Hooks相关(针对React 16.8+)
-
如何使用
useReducer
替代useState
?有什么场景更适合?- 答案:
useReducer
接受一个reducer
函数和一个初始状态值,返回当前的state
和一个dispatch
函数。- 当状态逻辑比较复杂,涉及到多个子值或者下一个状态依赖于前一个状态的计算时,
useReducer
更适合。例如,一个计数器组件,如果除了增加和减少计数,还有其他复杂的操作(如根据计数值进行不同状态的切换等),使用useReducer
可以使代码结构更清晰。
const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> Count: {state.count} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </div> ); }
- 答案:
-
useCallback
和useMemo
的区别和使用场景是什么?- 答案:
useCallback
返回一个记忆化的回调函数,主要用于避免在每次渲染时创建新的函数实例,从而提高性能,特别是当函数作为props
传递给子组件时,防止子组件不必要的重新渲染。useMemo
返回一个记忆化的值,用于缓存计算结果,当依赖项数组中的值发生变化时才重新计算。适用于计算开销较大的值的缓存,如复杂的数学计算、对象创建等。
const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]); const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
- 答案: