React面试题目详解:从入门到精通,这些题你都会吗?
前言
最近在准备React相关的面试,整理了一些高频面试题,分享给大家。这些题目涵盖了React的基础概念、生命周期、Hooks、性能优化等各个方面,希望能帮助到正在准备面试的小伙伴们!
目录
基础概念
1. React是什么?它的核心特性有哪些?
答案: React是一个用于构建用户界面的JavaScript库,由Facebook开发。
核心特性:
- 组件化:将UI拆分为独立、可复用的组件
- 虚拟DOM:通过虚拟DOM提高渲染性能
- 单向数据流:数据从父组件流向子组件
- JSX:允许在JavaScript中编写HTML样式的代码
详细解析: React的核心思想是组件化开发,每个组件都有自己的状态和生命周期。虚拟DOM是React性能优化的关键,它会在内存中维护一个虚拟的DOM树,通过diff算法找出需要更新的部分,然后批量更新真实DOM。
2. 什么是JSX?它和HTML有什么区别?
答案: JSX是JavaScript XML的缩写,是React中用来描述UI的语法糖。
主要区别:
- JSX中的属性使用驼峰命名(如
className、onClick) - JSX中的事件处理使用函数引用而不是字符串
- JSX中可以嵌入JavaScript表达式
- JSX最终会被编译为
React.createElement()调用
代码示例:
// JSX写法
const element = (
<div className="container">
<h1>Hello, {name}!</h1>
<button onClick={handleClick}>Click me</button>
</div>
);
// 编译后的JavaScript
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Hello, ', name, '!'),
React.createElement('button', { onClick: handleClick }, 'Click me')
);
生命周期
3. React 16.8+的生命周期有哪些?请详细说明
答案: React 16.8+引入了Hooks,但类组件仍然保留原有的生命周期。
挂载阶段:
constructor():构造函数,初始化state和绑定事件getDerivedStateFromProps():从props派生staterender():渲染组件componentDidMount():组件挂载完成
更新阶段:
getDerivedStateFromProps():props或state变化时调用shouldComponentUpdate():控制组件是否重新渲染render():重新渲染getSnapshotBeforeUpdate():获取更新前的快照componentDidUpdate():组件更新完成
卸载阶段:
componentWillUnmount():组件即将卸载
详细解析:
getDerivedStateFromProps是一个静态方法,不能访问组件实例,主要用于根据props的变化来更新state。getSnapshotBeforeUpdate在DOM更新之前调用,可以获取更新前的DOM信息。
Hooks
4. 什么是Hooks?常用的Hooks有哪些?
答案: Hooks是React 16.8引入的新特性,让函数组件也能使用state和其他React特性。
常用Hooks:
useState:管理组件状态useEffect:处理副作用useContext:使用ContextuseReducer:复杂状态管理useCallback:缓存函数useMemo:缓存值useRef:引用DOM元素或保存可变值
详细解析:
Hooks遵循"只在最顶层调用"和"只在React函数中调用"的规则。useEffect可以替代componentDidMount、componentDidUpdate和componentWillUnmount,通过依赖数组控制执行时机。
代码示例:
import React, { useState, useEffect, useCallback, useMemo } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 副作用处理
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
// 缓存函数
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
// 缓存计算值
const expensiveValue = useMemo(() => {
return count * 2;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Value: {expensiveValue}</p>
<button onClick={handleClick}>Increment</button>
<input value={text} onChange={e => setText(e.target.value)} />
</div>
);
}
5. useEffect的依赖数组是什么?如何正确使用?
答案:
useEffect的依赖数组决定了effect何时执行。
使用规则:
- 空数组
[]:只在组件挂载时执行一次 - 包含依赖的数组:依赖变化时执行
- 不传依赖数组:每次渲染后都执行
常见陷阱:
- 忘记添加依赖导致闭包问题
- 依赖数组包含对象或函数引用
正确使用示例:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
// 正确:userId变化时重新获取用户信息
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
// 错误:缺少依赖,可能导致闭包问题
useEffect(() => {
const timer = setInterval(() => {
console.log('Current user:', user); // user可能是旧值
}, 1000);
return () => clearInterval(timer);
}, []); // 应该包含user
// 正确:使用useCallback避免函数引用变化
const handleUserUpdate = useCallback((newData) => {
setUser(prev => ({ ...prev, ...newData }));
}, []);
return <div>{user?.name}</div>;
}
性能优化
6. React性能优化的方法有哪些?
答案: React性能优化主要从以下几个方面入手:
1. 避免不必要的重新渲染
- 使用
React.memo包装函数组件 - 使用
PureComponent或shouldComponentUpdate - 合理使用
useCallback和useMemo
2. 虚拟化长列表
- 使用
react-window或react-virtualized
3. 代码分割
- 使用
React.lazy和Suspense
4. 避免内联对象和函数
详细解析:
React.memo是一个高阶组件,它会对组件的props进行浅比较,如果props没有变化,组件就不会重新渲染。useCallback和useMemo可以避免在每次渲染时创建新的函数和对象。
代码示例:
// 使用React.memo优化
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
<button onClick={onUpdate}>Update</button>
</div>
);
});
// 父组件
function Parent() {
const [count, setCount] = useState(0);
const [data, setData] = useState([]);
// 使用useCallback避免函数引用变化
const handleUpdate = useCallback(() => {
setData(prev => [...prev, { id: Date.now(), name: 'New Item' }]);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<ExpensiveComponent data={data} onUpdate={handleUpdate} />
</div>
);
}
状态管理
7. React中的状态管理有哪些方案?
答案: React状态管理方案从简单到复杂可以分为:
1. 本地状态
useState:简单状态useReducer:复杂状态逻辑
2. 组件间状态共享
- Props传递
- Context API
3. 全局状态管理
- Redux
- Zustand
- MobX
- Recoil
详细解析:
对于小型应用,useState和useReducer就足够了。当需要在多个组件间共享状态时,可以使用Context API。对于大型应用,建议使用专门的状态管理库。
Context API示例:
// 创建Context
const ThemeContext = React.createContext();
// 提供者组件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 消费者组件
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff'
}}
>
Toggle Theme
</button>
);
}
实战题目
8. 实现一个自定义Hook:useLocalStorage
题目要求: 创建一个自定义Hook,用于在localStorage中存储和获取数据,类似于useState的用法。
答案:
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// 获取localStorage中的值,如果没有则使用初始值
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
// 设置localStorage的值
const setValue = (value) => {
try {
// 允许值是一个函数,这样我们就可以像useState一样使用
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue];
}
// 使用示例
function App() {
const [name, setName] = useLocalStorage('name', 'Guest');
const [count, setCount] = useLocalStorage('count', 0);
return (
<div>
<h1>Hello, {name}!</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Enter your name"
/>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
解析: 这个自定义Hook的关键点:
- 使用
useState的懒初始化函数来读取localStorage - 在
setValue中同时更新状态和localStorage - 支持函数式更新,保持与
useState一致的API - 使用try-catch处理可能的错误
9. 实现一个防抖Hook:useDebounce
题目要求: 创建一个防抖Hook,用于延迟执行函数,避免频繁调用。
答案:
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// 使用示例
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// 当debouncedSearchTerm变化时执行搜索
useEffect(() => {
if (debouncedSearchTerm) {
performSearch(debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
const performSearch = (term) => {
console.log('Searching for:', term);
// 实际的搜索逻辑
};
return (
<div>
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<p>Searching for: {debouncedSearchTerm}</p>
</div>
);
}
解析: 这个Hook的工作原理:
- 接收一个值和延迟时间作为参数
- 使用
useEffect监听值的变化 - 每次值变化时,清除之前的定时器并设置新的定时器
- 只有在延迟时间内没有新的值变化时,才会更新防抖值
总结
React面试题目涵盖了从基础概念到高级特性的各个方面。掌握这些知识点不仅有助于面试,更能提升日常开发能力。
关键要点:
- 深入理解React的核心概念和设计理念
- 熟练掌握Hooks的使用和最佳实践
- 了解性能优化的方法和原理
- 能够实现常见的自定义Hook
- 掌握状态管理的不同方案
希望这篇文章能帮助到正在准备React面试的小伙伴们!如果觉得有用,别忘了点赞和收藏哦~
标签: #React #前端面试 #JavaScript #前端开发 #面试题