是什么让我的鼠标悬停和反应 setState 变慢?
Whats making my mouseover and react setState slow?
我创建的任何一种“点击并拖动”类型的动作感觉都...有点不稳定。就像是口吃一样。我知道当我控制台记录数据时,数据是事件的即时数据,但状态更新中的延迟使它看起来更慢。
这是一个非常简单的例子:
const TestMouseOver = () => {
const [pos, setPos] = useState();
const onMouseDown = (e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
})
}
const onMouseMove = (e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
})
}
return (
<div
onMouseDown={(event) => onMouseDown(event)}
onMouseMove={(event) => onMouseMove(event)}
onMouseUp={() => setPos(null)}
>
{pos && <MoveBox position={pos} />}
</div>
);
};
在 MoveBox
中,我采用 pos
并将 div 的绝对 css 设置为位置(样式组件)。那里没什么好看的。它 工作 但它很慢。
我是否应该尝试构建某种 lerp 或动画以转到最后知道的线以平滑位置更新?
MouseEvents 不受限制:每次您的鼠标移动时,它都会调用您的 setState
函数。这种情况每秒可能发生数百次,导致 React 经常重新渲染,这显然不是最佳选择。
在最大速度下,React 不应重新渲染超过 16 毫秒的每帧,以确保您网站上的 60 FPS 动画流畅。
例如,您需要使用 lodash.throttle 来限制这些事件,并使用 useCallback
.
注册回调
示例代码:
const handleMouseMove = useCallback(lodash.throttle((e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
});
}, 16), []);
return (
<div onMouseMove={handleMouseMove}>{...}</div>
)
请注意 16
作为 lodash.throttle
的第二个参数:它确保该函数不会比每 16 毫秒更频繁地被调用。
您应该考虑的另一种可能的方法是使用浏览器原生 api requestAnimationFrame
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
这是针对您的特定案例的实施方式
const TestMouseOver = () => {
const [pos, setPos] = useState();
let ticking = false;
function onMouseMove(event) {
requestTick(event);
}
function onMouseDown(event) {
requestTick(event);
}
function requestTick(event) {
if (!ticking) {
requestAnimationFrame(update(event));
}
ticking = true;
}
function update(e) {
// reset the tick so we can capture next mouseevent
ticking = false;
setPos((prevState) => ({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
}))
}
return (
<div
onMouseDown={(event) => onMouseDown(event)}
onMouseMove={(event) => onMouseMove(event)}
onMouseUp={() => setPos(null)}
>
{pos && <MoveBox position={pos} />}
</div>
);
};
优点:
- 不需要任何外部库
- 以帧为单位执行。所以,UI 行为很顺利。
缺点:
- 不支持 IE9
- 无法定义油门持续时间,因为它与帧率有关
工作原理:
基本上,使用变量和 requestAnimationFrame 方法,我们确保状态以帧更新的速率更新
我创建的任何一种“点击并拖动”类型的动作感觉都...有点不稳定。就像是口吃一样。我知道当我控制台记录数据时,数据是事件的即时数据,但状态更新中的延迟使它看起来更慢。
这是一个非常简单的例子:
const TestMouseOver = () => {
const [pos, setPos] = useState();
const onMouseDown = (e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
})
}
const onMouseMove = (e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
})
}
return (
<div
onMouseDown={(event) => onMouseDown(event)}
onMouseMove={(event) => onMouseMove(event)}
onMouseUp={() => setPos(null)}
>
{pos && <MoveBox position={pos} />}
</div>
);
};
在 MoveBox
中,我采用 pos
并将 div 的绝对 css 设置为位置(样式组件)。那里没什么好看的。它 工作 但它很慢。
我是否应该尝试构建某种 lerp 或动画以转到最后知道的线以平滑位置更新?
MouseEvents 不受限制:每次您的鼠标移动时,它都会调用您的 setState
函数。这种情况每秒可能发生数百次,导致 React 经常重新渲染,这显然不是最佳选择。
在最大速度下,React 不应重新渲染超过 16 毫秒的每帧,以确保您网站上的 60 FPS 动画流畅。
例如,您需要使用 lodash.throttle 来限制这些事件,并使用 useCallback
.
示例代码:
const handleMouseMove = useCallback(lodash.throttle((e) => {
setPos({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
});
}, 16), []);
return (
<div onMouseMove={handleMouseMove}>{...}</div>
)
请注意 16
作为 lodash.throttle
的第二个参数:它确保该函数不会比每 16 毫秒更频繁地被调用。
您应该考虑的另一种可能的方法是使用浏览器原生 api requestAnimationFrame
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
这是针对您的特定案例的实施方式
const TestMouseOver = () => {
const [pos, setPos] = useState();
let ticking = false;
function onMouseMove(event) {
requestTick(event);
}
function onMouseDown(event) {
requestTick(event);
}
function requestTick(event) {
if (!ticking) {
requestAnimationFrame(update(event));
}
ticking = true;
}
function update(e) {
// reset the tick so we can capture next mouseevent
ticking = false;
setPos((prevState) => ({
x: e.clientX + document.body.scrollLeft,
y: e.clientY + document.body.scrollTop,
}))
}
return (
<div
onMouseDown={(event) => onMouseDown(event)}
onMouseMove={(event) => onMouseMove(event)}
onMouseUp={() => setPos(null)}
>
{pos && <MoveBox position={pos} />}
</div>
);
};
优点:
- 不需要任何外部库
- 以帧为单位执行。所以,UI 行为很顺利。
缺点:
- 不支持 IE9
- 无法定义油门持续时间,因为它与帧率有关
工作原理: 基本上,使用变量和 requestAnimationFrame 方法,我们确保状态以帧更新的速率更新