如何在 React 中使用 Throttle 和 Debounce 来提高性能
概述 Throttle和Debounce解决优化问题。 Throttle- 以特定频率跳过函数调用。 Debounce- 延迟函数调用,直到自上次调用以来经过一定时间。 _ 节流和去抖动方案:_ [](https://res.cloudinary.com/practicaldev/image/fetch/s--vx2mKwaL--/c_limit%2Cf_auto%2Cfl_progressive
概述
Throttle
和Debounce
解决优化问题。
Throttle
- 以特定频率跳过函数调用。
Debounce
- 延迟函数调用,直到自上次调用以来经过一定时间。
_ 节流和去抖动方案:_
[](https://res.cloudinary.com/practicaldev/image/fetch/s--vx2mKwaL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/ou8jq54ijrn6epvqm6ss.png)
油门使用示例:
1)如果用户调整了浏览器窗口的大小,我们需要改变网站的内容。
如果没有优化,会发生以下情况。在每个窗口调整大小事件上,都会调用窗口调整大小事件处理程序。因此,例如,如果用户在 10 秒内调整窗口大小,则可能会发生 100、200 等我们需要处理的事件。
Throttle
允许我们设置一个时间间隔,比该时间间隔更频繁地不会调用事件处理程序。如果我们使用Throttle
指定 1 秒的间隔,那么窗口调整大小事件处理程序的执行次数将为 10。
- 向用户显示页面滚动的百分比。当用户滚动页面时,会发生
scroll
个事件,我们需要处理这些事件。使用throttle
,我们可以通过设置时间间隔来减少处理的滚动事件的数量。
油门使用示例:
1)处理用户的搜索查询数据。
当用户输入搜索查询时,会为他们提供搜索选项。它以下列方式发生。
当更改用户输入的文本时,将向服务器发送一个请求,我们将在其中传输已打印的字符。然后我们从服务器获得可能的搜索查询选项的响应并将它们显示给用户。
每次用户更改文本时,都会调用一个事件处理程序,其中向服务器发送一个请求。
为了优化发送到服务器的请求数,我们使用Debounce
。
当用户更改文本时,使用Debounce
允许我们创建一个计时器,例如 1 秒。如果 1 秒过去并且用户没有再次更改文本,则调用事件处理程序并将请求发送到服务器。如果用户在 1 秒内第二次更改文本,则重置第一个计时器并在 1 秒内再次创建一个新计时器。
因此,如果用户快速编辑搜索文本(不到 1 秒),那么在用户停止输入后,请求将只发送到服务器一次。
- 将分析数据发送到服务器。例如,用户在站点周围移动鼠标,我们将鼠标坐标写入一个数组,之后
Debounce
允许我们在客户端停止移动鼠标后才向服务器发送有关客户端鼠标移动的信息。
因此,在本文中,我将向您展示如何在 React 应用程序中使用Throttle
和Debounce
。
第 1 步 - 申请模板
使用create-react-app
创建一个应用模板并运行它:
npx create-react-app throttle-debounce
cd throttle-debounce
npm start
进入全屏模式 退出全屏模式
我们用我们的样式替换App.css
文件的内容:
body {
display: flex;
justify-content: center;
width: 100%;
}
h1 {
text-align: center;
margin: 0.5rem 0;
}
.l-scroll {
overflow-y: scroll;
overflow-x: hidden;
width: 380px;
height: 200px;
margin-top: 0.5rem;
}
.scroll-content {
width: 100%;
background-color: bisque;
padding: 0 1rem;
}
.l-scroll::-webkit-scrollbar {
width: 10px;
height: 8px;
background-color: darkturquoise;
}
.l-scroll::-webkit-scrollbar-thumb {
background-color: blueviolet;
}
进入全屏模式 退出全屏模式
让我们将App.js
文件的内容替换为我们的应用程序模板:
import './App.css';
import { useMemo } from 'react';
function App() {
return (
<>
<h1>Throttle & Debounce</h1>
<div className="l-scroll">
<div className="scroll-content">
<TallContent />
</div>
</div>
</>
);
}
// High height scrollable content
function TallContent(){
const dataElements = useMemo(() => {
const genData = [];
for(let i=1; i<=200; i++){
genData.push(
<div key={i}>Line: {i}</div>
);
}
return genData;
}, []);
return(
<>
{dataElements}
</>
);
}
export default App;
进入全屏模式 退出全屏模式
应用程序模板已经准备好,让我们继续第二步——通常的滚动事件处理程序。
第 2 步 - 常规事件处理程序
在这里,我们将为scroll
个事件添加一个常用的事件处理程序,并计算用户滚动页面元素时对该处理程序的调用次数。
让我们将事件处理程序调用次数的状态添加到App
组件中:
// At the beginning of the file
import { useState, useMemo } from 'react';
// Inside the App component
const [scrollHandleCount, setScrollHandleCount] = useState(0);
进入全屏模式 退出全屏模式
然后我们添加一个滚动事件处理程序,为此我们将onScroll
属性添加到h1
标题下的元素:
// Before
<div className="l-scroll">
...
</div>
// After
<div className="l-scroll" onScroll={handleScroll}>
...
</div>
进入全屏模式 退出全屏模式
我们还将为App
组件添加一个处理handleScroll
事件的函数:
function handleScroll(){
handleUsualScroll();
}
进入全屏模式 退出全屏模式
在handleScroll
函数中,我们放置了一个函数来处理通常的事件。让我们将此函数添加到我们的App
组件中:
function handleUsualScroll(){
setScrollHandleCount((prevState) => {
return ++prevState;
});
}
进入全屏模式 退出全屏模式
它仍然只是向用户显示计数器的状态,为此我们在h1
标题下添加一行代码:
<span>
Usual scroll handle count: {scrollHandleCount}
</span>
<br />
进入全屏模式 退出全屏模式
现在,当滚动页面上的元素时,我们应该看到调用handleUsualScroll()
函数的次数。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--4KexX0IS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/z0oq1rvk4t6w0b17km46.gif)
目前App
组件的完整代码:
function App() {
const [scrollHandleCount, setScrollHandleCount] = useState(0);
return (
<>
<h1>Throttle & Debounce</h1>
<span>
Usual scroll handle count: {scrollHandleCount}
</span>
<br />
<div className="l-scroll" onScroll={handleScroll}>
<div className="scroll-content">
<TallContent />
</div>
</div>
</>
);
function handleScroll(){
handleUsualScroll();
}
function handleUsualScroll(){
setScrollHandleCount((prevState) => {
return ++prevState;
});
}
}
进入全屏模式 退出全屏模式
第 3 步 - 带节流的事件处理程序
在我们的例子中,Throttle
事件处理程序应该导致scrollThrottleHandleCount
计数器递增,而 skip 调用以在特定时间间隔递增计数器。
为了实现我们的计划,我们需要一个计时器,在开始时,状态Throlle
进入In progress
。在这种情况下,如果状态是In Progerss
,则跳过用户事件(滚动页面元素)的处理。
一旦计时器触发,Throttle
状态就会变为Not in progress
,这意味着我们的处理程序将再次处理用户事件。因此,以指定的时间间隔跳过用户事件。
我们实现上述:
// Add useRef to store inProgress state
import { useState, useRef, useMemo } from 'react';
进入全屏模式 退出全屏模式
接下来,在App
组件中,将事件处理程序调用计数器的状态添加为Throttle
和ref
以存储inProgress
状态:
// Number of event handler calls with Throttle
const [
scrollThrottleHandleCount,
setScrollThrottleHandleCount
] = useState(0);
// Keeping the state in progress
const throttleInProgress = useRef();
进入全屏模式 退出全屏模式
这里需要注意的是,throttleInProgress
是与定时器相关联的副作用的一部分,这意味着我们会将状态存储在ref
对象中,因为useRef
返回的对象存在于组件的整个生命周期中,而更改useRef
返回的对象的current
属性时,没有额外的渲染组件,而不是useState
。
现在让我们将带有Throttle
的事件处理程序本身添加到App
组件中:
function handleThrottleScroll(){
// If the state is inProgress - exit the function,
// skip event processing
if(throttleInProgress.current){ return; }
// Set inProgress to true and start the timer
throttleInProgress.current = true;
setTimeout(() => {
// Increment the throttleHandleCount
// state by one
setScrollThrottleHandleCount((prevState) => {
return ++prevState;
});
// Set inProgress to false, which means
// that setTimeout will work
// again on the next run
throttleInProgress.current = false;
}, 500);
}
进入全屏模式 退出全屏模式
剩下 2 个简单的步骤:向用户添加Throttle
的计数器状态显示并将handleThrottleScroll()
添加到handleScroll()
:
// After heading h1
<span>
Throttle scroll handle count: {scrollThrottleHandleCount}
</span>
// In the handleScroll() function after handleUsualScroll();
handleThrottleScroll();
进入全屏模式 退出全屏模式
结果,我们将得到:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--5v5KFjaV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/4absm13nqx2l8n2amd3x.gif)
正常的事件处理程序调用应用程序的业务逻辑 181 次,而Throttle
只有 9 次。
带Throttle
的App
组件的完整代码:
function App() {
const [scrollHandleCount, setScrollHandleCount] = useState(0);
const [
scrollThrottleHandleCount,
setScrollThrottleHandleCount
] = useState(0);
const throttleInProgress = useRef();
return (
<>
<h1>Throttle & Debounce</h1>
<span>
Usual scroll handle count: {scrollHandleCount}
</span>
<br />
<span>
Throttle scroll handle count: {scrollThrottleHandleCount}
</span>
<br />
<div className="l-scroll" onScroll={handleScroll}>
<div className="scroll-content">
<TallContent />
</div>
</div>
</>
);
function handleScroll(){
handleUsualScroll();
handleThrottleScroll();
}
function handleUsualScroll(){
setScrollHandleCount((prevState) => {
return ++prevState;
});
}
function handleThrottleScroll(){
if(throttleInProgress.current){ return; }
throttleInProgress.current = true;
setTimeout(() => {
setScrollThrottleHandleCount((prevState) => {
return ++prevState;
});
throttleInProgress.current = false;
}, 500);
}
}
进入全屏模式 退出全屏模式
让我们继续最后一步 - 实现Debounce
事件处理程序。
第 4 步 - 带去抖动的事件处理程序
在我们的示例中,Debounce
延迟递增scrollDebounceHandleCount
计数器,直到经过一定时间 since the last call of the event handler***。
让我们将调用次数的状态添加到事件处理程序Debounce
、ref
以将计时器 ID 存储在App
组件中:
const [
scrollDebounceHandleCount,
setScrollDebounceHandleCount
] = useState(0);
const timerDebounceRef = useRef();
进入全屏模式 退出全屏模式
然后我们向用户显示scrollDebounceHandleCount
的数量并将我们的handleDebounceScroll()
方法添加到handleScroll()
:
// After h1
<span>
Debound scroll handle count: {scrollDebounceHandleCount}
</span>
// In handleScroll() function
handleDebounceScroll();
进入全屏模式 退出全屏模式
剩下的就是写handleDebounceScroll
函数:
function handleDebounceScroll(){
// If the timer ID is set, reset the timer
if(timerDebounceRef.current){
clearTimeout(timerDebounceRef.current);
}
// We start the timer, the returned timer ID
// is written to timerDebounceRef
timerDebounceRef.current = setTimeout(() => {
// Increasing the counter for the number of
// executions of the business logic
// of the application with Debounce
setScrollDebounceHandleCount((prevState) => {
return ++prevState;
});
}, 500);
}
进入全屏模式 退出全屏模式
因此,只有当用户停止滚动页面元素超过或等于 500 毫秒时,Debounce
计数器才会增加:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--hIacM0v5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/zpoagbjqn20994mt9npz.gif)
App
组件全文:
function App() {
const [scrollHandleCount, setScrollHandleCount] = useState(0);
const [
scrollThrottleHandleCount,
setScrollThrottleHandleCount
] = useState(0);
const [
scrollDebounceHandleCount,
setScrollDebounceHandleCount
] = useState(0);
const throttleInProgress = useRef();
const timerDebounceRef = useRef();
return (
<>
<h1>Throttle & Debounce</h1>
<span>
Usual scroll handle count: {scrollHandleCount}
</span>
<br />
<span>
Throttle scroll handle count: {scrollThrottleHandleCount}
</span>
<br />
<span>
Debound scroll handle count: {scrollDebounceHandleCount}
</span>
<div className="l-scroll" onScroll={handleScroll}>
<div className="scroll-content">
<TallContent />
</div>
</div>
</>
);
function handleScroll(){
handleUsualScroll();
handleThrottleScroll();
handleDebounceScroll();
}
function handleUsualScroll(){
setScrollHandleCount((prevState) => {
return ++prevState;
});
}
function handleThrottleScroll(){
if(throttleInProgress.current){ return; }
throttleInProgress.current = true;
setTimeout(() => {
setScrollThrottleHandleCount((prevState) => {
return ++prevState;
});
throttleInProgress.current = false;
}, 500);
}
function handleDebounceScroll(){
if(timerDebounceRef.current){
clearTimeout(timerDebounceRef.current);
}
timerDebounceRef.current = setTimeout(() => {
setScrollDebounceHandleCount((prevState) => {
return ++prevState;
});
}, 500);
}
}
进入全屏模式 退出全屏模式
订阅博客,点赞,添加到书签。
不要忘记独角兽。
感谢您的关注!
更多推荐
所有评论(0)