1. 自定义事件与原生DOM事件交互场景
避免冲突
- 命名规范:在React中,自定义事件命名可以采用特定前缀,如
my-
,与原生DOM事件名称区分。例如,原生事件有click
,自定义事件可命名为my-custom-event
。
- 事件处理函数隔离:在组件中,为原生DOM事件和自定义事件分别设置不同的处理函数。例如:
import React, { useEffect } from 'react';
const MyComponent = () => {
const handleNativeClick = () => {
console.log('原生click事件触发');
};
const handleCustomEvent = () => {
console.log('自定义事件触发');
};
useEffect(() => {
const customEvent = new CustomEvent('my-custom-event');
document.addEventListener('my-custom-event', handleCustomEvent);
return () => {
document.removeEventListener('my-custom-event', handleCustomEvent);
};
}, []);
return <button onClick={handleNativeClick}>点击</button>;
};
export default MyComponent;
事件委托
- 原生DOM事件委托:在React中,对于原生DOM事件的委托,可利用React自身的事件系统。例如,有多个子元素都需要绑定
click
事件,可以在父元素上统一绑定click
事件,通过event.target
判断具体触发的子元素。
import React from 'react';
const ParentComponent = () => {
const handleClick = (event) => {
if (event.target.tagName === 'LI') {
console.log(`点击了列表项: ${event.target.textContent}`);
}
};
return (
<ul onClick={handleClick}>
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
</ul>
);
};
export default ParentComponent;
- 自定义事件委托:对于自定义事件委托,可以通过在祖先元素上监听自定义事件来实现。例如:
import React, { useEffect } from 'react';
const ParentComponent = () => {
const handleCustomEvent = () => {
console.log('父组件捕获到自定义事件');
};
useEffect(() => {
document.addEventListener('my-custom-event', handleCustomEvent);
return () => {
document.removeEventListener('my-custom-event', handleCustomEvent);
};
}, []);
return (
<div>
<ChildComponent />
</div>
);
};
const ChildComponent = () => {
const triggerCustomEvent = () => {
const customEvent = new CustomEvent('my-custom-event');
document.dispatchEvent(customEvent);
};
return <button onClick={triggerCustomEvent}>触发自定义事件</button>;
};
export default ParentComponent;
2. 大规模事件触发场景性能调优策略
防抖(Debounce)
- 原理:在事件触发后,等待一定时间(如300ms),如果这段时间内事件再次触发,则重新计时,只有在等待时间结束且没有新的事件触发时,才执行真正的处理函数。
- 代码示例:
import React, { useState } from 'react';
const debounce = (func, delay) => {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
};
const MyComponent = () => {
const [inputValue, setInputValue] = useState('');
const handleInputChange = debounce((event) => {
console.log('防抖后处理输入变化:', event.target.value);
setInputValue(event.target.value);
}, 300);
return <input onChange={handleInputChange} />;
};
export default MyComponent;
节流(Throttle)
- 原理:在事件频繁触发的过程中,每隔一定时间(如200ms)执行一次处理函数,无论事件触发多么频繁,都以固定时间间隔执行。
- 代码示例:
import React, { useState } from 'react';
const throttle = (func, limit) => {
let inThrottle;
return function() {
const context = this;
const args = arguments;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
};
const MyComponent = () => {
const [scrollY, setScrollY] = useState(0);
const handleScroll = throttle(() => {
setScrollY(window.pageYOffset);
console.log('节流后处理滚动事件:', window.pageYOffset);
}, 200);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div>
<p>滚动位置: {scrollY}</p>
</div>
);
};
export default MyComponent;
批量更新
- 原理:在React中,默认情况下,React会在事件处理函数结束后批量更新状态,以提高性能。但在某些异步操作中,可能需要手动进行批量更新。例如,在
setTimeout
或Promise回调中多次调用setState
,React默认会每次都进行更新,这会导致性能下降。通过unstable_batchedUpdates
(React 18之前)或flushSync
(React 18及之后)可以将多次setState
操作合并为一次更新。
- React 18之前代码示例:
import React, { useState } from 'react';
import ReactDOM from'react-dom';
import { unstable_batchedUpdates } from'react-dom';
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
unstable_batchedUpdates(() => {
setCount(count + 1);
setCount(count + 1);
});
};
return (
<div>
<p>计数: {count}</p>
<button onClick={handleClick}>增加计数</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById('root'));
import React, { useState } from'react';
import ReactDOM from'react-dom/client';
import { flushSync } from'react-dom';
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
flushSync(() => {
setCount(count + 1);
setCount(count + 1);
});
};
return (
<div>
<p>计数: {count}</p>
<button onClick={handleClick}>增加计数</button>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyComponent />);