前端八股文-react篇
React 基础概念
1. React 的生命周期方法有哪些?
React 类组件的生命周期分为三个阶段:
挂载阶段:
constructor()
:初始化 state 和绑定方法static getDerivedStateFromProps()
:在 render 前调用render()
:渲染组件componentDidMount()
:组件挂载后调用,适合发起网络请求
更新阶段:
static getDerivedStateFromProps()
:props 变化时调用shouldComponentUpdate()
:决定组件是否重新渲染render()
:渲染组件getSnapshotBeforeUpdate()
:在 DOM 更新前捕获信息componentDidUpdate()
:组件更新后调用
卸载阶段:
componentWillUnmount()
:组件卸载前清理定时器、取消订阅等
2. React 的函数组件和类组件有什么区别?
特性 | 函数组件 | 类组件 |
---|---|---|
语法 | 普通函数 | ES6 class 继承 React.Component |
状态管理 | 使用 Hooks (useState) | 使用 this.state |
生命周期 | 使用 useEffect 等 Hooks | 有完整的生命周期方法 |
this 关键字 | 无 | 需要绑定 this |
性能 | 更轻量 | 相对较重 |
代码量 | 更简洁 | 相对冗长 |
3. 什么是虚拟 DOM?React 如何工作?
虚拟 DOM:
- 是真实 DOM 的轻量级 JavaScript 表示
- 用 JavaScript 对象描述 DOM 树结构
- 通过 diff 算法比较新旧虚拟 DOM 的差异
工作流程:
- 当状态改变时,重新渲染组件生成新的虚拟 DOM
- 比较新旧虚拟 DOM(diff 算法)
- 计算出最小变更集
- 批量更新真实 DOM
4. React 的 key 有什么作用?
- 唯一标识:帮助 React 识别哪些元素被改变、添加或删除
- 优化性能:减少不必要的 DOM 操作
- 正确使用:
- 应该在数组内的元素上设置 key
- key 应该在兄弟节点间唯一
- 不推荐使用 index 作为 key(除非列表静态不变)
React Hooks
5. 常用的 React Hooks 有哪些?
-
useState:管理组件状态
const [state, setState] = useState(initialState);
-
useEffect:处理副作用
useEffect(() => {// 副作用代码return () => { /* 清理函数 */ }; }, [dependencies]);
-
useContext:访问 Context
const value = useContext(MyContext);
-
useReducer:复杂状态逻辑
const [state, dispatch] = useReducer(reducer, initialState);
-
useCallback:记忆函数
const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
-
useMemo:记忆值
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
-
useRef:访问 DOM 或保存可变值
const refContainer = useRef(initialValue);
6. useEffect 和 useLayoutEffect 的区别?
特性 | useEffect | useLayoutEffect |
---|---|---|
执行时机 | 在浏览器绘制后异步执行 | 在 DOM 更新后同步执行 |
用途 | 数据获取、订阅等副作用 | 需要同步读取/操作 DOM 的情况 |
对用户的影响 | 不会阻塞页面渲染 | 会阻塞页面渲染 |
使用频率 | 常用 | 特殊场景使用 |
7. 如何自定义 Hook?
自定义 Hook 是一个以 “use” 开头的函数,可以调用其他 Hook:
function useCustomHook(initialValue) {const [value, setValue] = useState(initialValue);const doubleValue = useMemo(() => value * 2, [value]);const increment = useCallback(() => {setValue(v => v + 1);}, []);return { value, doubleValue, increment };
}
使用规则:
- 只在 React 函数组件或自定义 Hook 中调用 Hook
- 只在最顶层调用 Hook
- 自定义 Hook 必须以 “use” 开头
React 进阶知识
8. React 的 diff 算法原理是什么?
React 的 diff 算法基于两个假设:
- 不同类型的元素会产生不同的树
- 通过 key 属性标识稳定的子元素
优化策略:
- Tree Diff:只比较同级节点,不跨级比较
- Component Diff:
- 相同类组件继续比较
- 不同类组件直接替换
- Element Diff:
- 使用 key 标识元素
- 移动而非重建相同 key 的元素
9. React 的性能优化手段有哪些?
-
使用 React.memo:缓存函数组件
const MyComponent = React.memo(function MyComponent(props) {/* 使用 props 渲染 */ });
-
使用 useMemo/useCallback:避免不必要的计算和渲染
-
避免内联函数和对象:防止子组件不必要重渲染
-
代码分割:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
-
虚拟化长列表:使用 react-window 或 react-virtualized
-
避免使用索引作为 key
-
使用 shouldComponentUpdate/PureComponent
10. React 的 Context 是什么?如何使用?
Context 提供了一种在组件树中共享值的方式,而不必显式地通过组件树的逐层传递 props。
创建 Context:
const MyContext = React.createContext(defaultValue);
提供 Context:
<MyContext.Provider value={/* 某个值 */}>{/* 子组件 */}
</MyContext.Provider>
消费 Context:
// 类组件
static contextType = MyContext;
// 或
<MyContext.Consumer>{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>// 函数组件
const value = useContext(MyContext);
11. Redux 的核心概念和工作流程
核心概念:
- Store:保存应用状态的单一对象
- Action:描述发生什么的对象,必须有 type 字段
- Reducer:纯函数,接收旧 state 和 action,返回新 state
- Dispatch:触发 action 的方法
工作流程:
- 用户触发 UI 事件
- dispatch 一个 action
- reducer 根据 action 类型处理状态
- store 保存新状态
- 订阅 store 的组件重新渲染
Redux 三大原则:
- 单一数据源
- State 是只读的
- 使用纯函数修改 state
12. React Router 的核心概念和用法
核心组件:
<BrowserRouter>
:使用 HTML5 history API 的路由器<Route>
:路由配置核心组件<Switch>
:渲染第一个匹配的路由<Link>
:导航链接<Redirect>
:重定向
基本用法:
<BrowserRouter><Switch><Route exact path="/" component={Home} /><Route path="/about" component={About} /><Route path="/users/:id" component={User} /><Redirect to="/" /></Switch>
</BrowserRouter>
Hooks API:
useHistory
:访问 history 对象useLocation
:访问 location 对象useParams
:访问路由参数useRouteMatch
:访问匹配信息
React 实战问题
13. 如何解决 props 层层传递(prop drilling)问题?
- 使用 Context API:适合全局共享的数据
- 状态管理库:Redux、MobX 等
- 组件组合:通过 children 或 render props
- 状态提升:将共享状态提升到最近的共同祖先
- 自定义 Hooks:封装共享逻辑
14. React 中的错误边界是什么?
错误边界是捕获子组件树 JavaScript 错误的 React 组件:
class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, info) {logErrorToService(error, info);}render() {if (this.state.hasError) {return <h1>Something went wrong.</h1>;}return this.props.children; }
}
使用方式:
<ErrorBoundary><MyComponent />
</ErrorBoundary>
注意:
- 无法捕获以下错误:
- 事件处理函数
- 异步代码
- 服务端渲染
- 错误边界自身抛出的错误
15. React 18 的新特性有哪些?
-
并发渲染(Concurrent Rendering):
- 可中断渲染
- 自动批处理更新
- 过渡更新(startTransition)
-
新的 Root API:
const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />);
-
新的 Hooks:
useId
:生成唯一 IDuseTransition
:标记非紧急更新useDeferredValue
:延迟更新某些值useSyncExternalStore
:外部存储集成useInsertionEffect
:CSS-in-JS 库使用
-
Suspense 增强:
- 支持数据获取
- 服务端流式渲染
-
改进的自动批处理:更多场景下的自动批处理