面试题答案
一键面试React批处理机制原理
React 的批处理机制是为了提高性能而设计的。在 React 中,当事件处理函数被触发时,React 会将多个 setState 调用合并成一次批量更新。这意味着在同一个事件循环内,多次调用 setState 不会立即触发重新渲染,而是将这些状态更新操作放入队列中,在合适的时机(事件处理函数结束后)统一进行处理,从而减少不必要的重新渲染次数,提高性能。
例如,在一个点击事件处理函数中多次调用 setState:
import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
在上述代码中,虽然调用了三次 setCount,但实际上只会触发一次重新渲染,因为 React 批处理机制将这些 setState 调用合并了。
复杂场景下优化性能的实现思路
- 自定义批处理:可以使用
unstable_batchedUpdates
(React 18 之前)或者flushSync
(React 18 及之后)来手动控制批处理。- React 18 之前使用
unstable_batchedUpdates
:
- React 18 之前使用
import React, { useState, useRef } from'react';
import ReactDOM from'react-dom';
function App() {
const [count, setCount] = useState(0);
const batch = useRef(null);
if (!batch.current) {
batch.current = ReactDOM.unstable_batchedUpdates;
}
const handleClick = () => {
batch.current(() => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
});
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
- **React 18 及之后使用 `flushSync`**:
import React, { useState } from'react';
import { flushSync } from'react-dom';
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
flushSync(() => {
setCount(count + 1);
});
flushSync(() => {
setCount(count + 1);
});
flushSync(() => {
setCount(count + 1);
});
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
- 使用
requestAnimationFrame
:在复杂动画场景中,可以利用requestAnimationFrame
来控制动画的更新,避免频繁的重新渲染。例如,在一个动画组件中:
import React, { useState, useEffect } from'react';
function AnimatedComponent() {
const [position, setPosition] = useState(0);
useEffect(() => {
const updatePosition = () => {
setPosition((prevPosition) => prevPosition + 1);
if (position < 100) {
requestAnimationFrame(updatePosition);
}
};
requestAnimationFrame(updatePosition);
return () => {
// 清理函数,取消动画
};
}, [position]);
return (
<div style={{ transform: `translateX(${position}px)` }}>
Moving Element
</div>
);
}
export default AnimatedComponent;
- 使用
shouldComponentUpdate
或React.memo
:通过控制组件的更新条件,避免不必要的重新渲染。- 类组件使用
shouldComponentUpdate
:
- 类组件使用
import React, { Component } from'react';
class MyComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
// 这里可以根据具体业务逻辑判断是否需要更新
return nextProps.value!== this.props.value || nextState.count!== this.state.count;
}
render() {
return <div>{this.props.value}</div>;
}
}
export default MyComponent;
- **函数组件使用 `React.memo`**:
import React from'react';
const MyComponent = React.memo((props) => {
return <div>{props.value}</div>;
});
export default MyComponent;