概述

ThrottleDebounce解决优化问题。

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。

  1. 向用户显示页面滚动的百分比。当用户滚动页面时,会发生scroll个事件,我们需要处理这些事件。使用throttle,我们可以通过设置时间间隔来减少处理的滚动事件的数量。

油门使用示例:

1)处理用户的搜索查询数据。

当用户输入搜索查询时,会为他们提供搜索选项。它以下列方式发生。

当更改用户输入的文本时,将向服务器发送一个请求,我们将在其中传输已打印的字符。然后我们从服务器获得可能的搜索查询选项的响应并将它们显示给用户。

每次用户更改文本时,都会调用一个事件处理程序,其中向服务器发送一个请求。

为了优化发送到服务器的请求数,我们使用Debounce

当用户更改文本时,使用Debounce允许我们创建一个计时器,例如 1 秒。如果 1 秒过去并且用户没有再次更改文本,则调用事件处理程序并将请求发送到服务器。如果用户在 1 秒内第二次更改文本,则重置第一个计时器并在 1 秒内再次创建一个新计时器。

因此,如果用户快速编辑搜索文本(不到 1 秒),那么在用户停止输入后,请求将只发送到服务器一次。

  1. 将分析数据发送到服务器。例如,用户在站点周围移动鼠标,我们将鼠标坐标写入一个数组,之后Debounce允许我们在客户端停止移动鼠标后才向服务器发送有关客户端鼠标移动的信息。

因此,在本文中,我将向您展示如何在 React 应用程序中使用ThrottleDebounce

第 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组件中,将事件处理程序调用计数器的状态添加为Throttleref以存储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 次。

ThrottleApp组件的完整代码:

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***。

让我们将调用次数的状态添加到事件处理程序Debounceref以将计时器 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);
    }
}

进入全屏模式 退出全屏模式

订阅博客,点赞,添加到书签。

不要忘记独角兽。

感谢您的关注!

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐