Heim > Web-Frontend > js-Tutorial > Die Kunst von Smooth UX: Entprellen und Drosseln für eine leistungsfähigere Benutzeroberfläche

Die Kunst von Smooth UX: Entprellen und Drosseln für eine leistungsfähigere Benutzeroberfläche

DDD
Freigeben: 2025-01-01 02:05:09
Original
897 Leute haben es durchsucht

Github-Code-Repository

In der schnelllebigen Welt finden die meisten Jobs, die wir erledigen, im Internet statt, und zwar schnell. Umso wichtiger wird es, ein nahtloses und reibungsloses Benutzererlebnis zu schaffen. Verbraucher lieben eine Benutzeroberfläche, die schnell und ohne Verzögerungen funktioniert. Es ist möglich, wenn auch schwierig, ein nahezu perfektes Erlebnis zu erreichen. Haben Sie schon einmal von Event Loops gehört?

In JavaScript ist die Ereignisschleife ein grundlegendes Konzept, das die Reihenfolge der Codeausführung verwaltet, Prozesse sammelt, Anweisungen in Unteraufgaben in der Warteschlange platziert und asynchrone Vorgänge effizient ausführt. Hier ist eine kurze Aufschlüsselung der Funktionsweise einer Ereignisschleife:

  • Aufrufstapel: Alle Funktionen werden beim Aufruf diesem Stapel hinzugefügt, und der Kontrollfluss kehrt von der Funktion zurück, sie wird aus dem Stapel entfernt
  • Heap: Allen Variablen und Objekten wird Speicher von diesem Heap zugewiesen
  • Warteschlange: Eine Liste von Nachrichten/Anweisungen – die nacheinander ausgeführt werden

Diese Ereignisschleife überprüft kontinuierlich den Aufrufstapel. Die Ausführung eines JavaScript-Codes wird fortgesetzt, bis der Aufrufstapel leer ist.

Die Ereignisbehandlung ist ein sehr wichtiger Teil beim Erstellen von JavaScript-Anwendungen. In einer solchen Anwendung müssen wir möglicherweise mehrere Ereignisse einer UI-Komponente zuordnen.

The art of Smooth UX : Debouncing and Throttling for a more performant UI

Stellen Sie sich vor...

Sie haben eine Schaltfläche in einer Benutzeroberfläche, die dabei hilft, eine Tabelle mit den neuesten Nachrichten zum Thema Sport zu füllen. Dazu müssen Sie Folgendes tun:

  • Klicken Sie auf eine Schaltfläche (Verknüpfen Sie den Ereignishandler „Klick“ mit einer Schaltfläche.
  • Ergebnisse von einer API abrufen
  • Parsen Sie die Ausgabe (Json) und zeigen Sie sie an

Diese 3 Prozesse sind synchron miteinander verkettet. Nun würde ein wiederholtes Drücken der Schaltfläche mehrere API-Aufrufe bedeuten – was dazu führen würde, dass die Benutzeroberfläche für einige Sekunden blockiert wird – eine scheinbar verzögerte Benutzererfahrung.

Dies ist ein guter Anwendungsfall für Ansätze wie Entprellen und Drosseln. Bei Ereignissen wie diesem, die eine Kette komplexer Ereignisse auslösen, können wir solche Manöver verwenden, um die Anzahl der Aufrufe der API zu begrenzen, oder im Allgemeinen die Geschwindigkeit zu begrenzen, mit der wir ein Ereignis verarbeiten.

Was ist Entprellen im Vergleich zu Drosselung?

Entprellen:Verzögern der Ausführung einer Funktion, bis eine bestimmte Abklingzeit seit dem letzten Ereignis verstrichen ist.

Zum Beispiel:

Wenn wir handleOnPressKey() für 2 Sekunden entprellen, wird es nur ausgeführt, wenn der Benutzer für 2 Sekunden keine Tasten mehr drückt.

Szenario:

  • Erster Tastendruck: Starten Sie einen 2000-ms-Timer, um handleOnPressKey() aufzurufen.
  • Weiterer Tastendruck innerhalb von 1000 ms: Der Timer wird zurückgesetzt; Wir warten weitere 2000 ms nach diesem letzten Tastendruck.
  • Kein Tastendruck für 2000 ms: Der Timer läuft ab und handleOnPressKey() wird aufgerufen.

Codeausschnitt:

let debounceTimer; // Timer reference

const handleOnPressKey = () => {
    console.log("Key pressed and debounce period elapsed!");
};

const debouncedKeyPress = () => {
    // Clear any existing timer
    clearTimeout(debounceTimer);

    // Start a new debounce timer
    debounceTimer = setTimeout(() => {
        handleOnPressKey(); // Execute the function after cooldown
    }, 2000); // Cooldown period of 2000ms
};

// Attach debouncedKeyPress to keypress events
document.getElementById("input").addEventListener("keypress", debouncedKeyPress);
Nach dem Login kopieren
Nach dem Login kopieren

Drosselung: Sicherstellen, dass eine Funktion höchstens einmal innerhalb eines bestimmten Zeitraums aufgerufen wird, unabhängig davon, wie oft das Ereignis auftritt.

Zum Beispiel:

Wenn wir handleOnScroll() mit einem 2-Sekunden-Intervall drosseln, wird die Funktion höchstens einmal alle 2 Sekunden ausgeführt, auch wenn das Scroll-Ereignis innerhalb dieses Zeitraums mehrmals ausgelöst wird.

Szenario:

  • Anfängliches Scroll-Ereignis: handleOnScroll() wird aufgerufen und eine Abklingzeit von 2000 ms beginnt.
  • Nachfolgende Scroll-Ereignisse innerhalb von 2000 ms: Diese werden ignoriert, da die Abklingzeit aktiv ist.
  • Scroll-Ereignis nach 2000 ms: handleOnScroll() wird erneut aufgerufen.

Codebeispiel:

let throttleTimer; // Timer reference

const handleOnScroll = () => {
    console.log("Scroll event processed!");
};

const throttledScroll = () => {
    if (!throttleTimer) {
        handleOnScroll(); // Execute the function immediately
        throttleTimer = setTimeout(() => {
            throttleTimer = null; // Reset timer after cooldown
        }, 2000); // Cooldown period of 2000ms
    }
};

// Attach throttledScroll to scroll events
document.addEventListener("scroll", throttledScroll);
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt lasst uns etwas bauen

Bei diesem Projekt handelt es sich um eine moderne To-Do-Liste-Anwendung, die entwickelt wurde, um die Konzepte der Entprellung und Drosselung bei der Ereignisverarbeitung zu untersuchen. Es verfügt über das Hinzufügen von Aufgaben in Echtzeit, Suchfunktionen auf Basis von Fuse.js und ein Dropdown-Menü für dynamische Vorschläge.

The art of Smooth UX : Debouncing and Throttling for a more performant UI

Werfen wir kurz einen Blick auf den HTML-Code, bevor wir uns dem kritischeren script.js zuwenden

Wir haben TailwindCSS für schnelles Styling verwendet. Sie können ihre Dokumentation hier einsehen: Tailwind Documentation – sie ist enorm hilfreich für die Erstellung schneller Prototypen

  • Kopfzeile: Die Kopfzeile enthält den Titel der Seite.
  • Eingabefeld: Ein Eingabefeld zum Hinzufügen von Notizen, gestaltet mit Tailwind CSS.
  • Dropdown für Vorschläge: Ein verstecktes Dropdown-Menü, das Vorschläge anzeigt, während der Benutzer tippt.
  • Statische Aufgabenliste: Eine Liste zur Anzeige der hinzugefügten Aufgaben.
  • Skripte: Enthält die Fuse.js-Bibliothek für die Fuzzy-Suche und die script.js-Datei für benutzerdefinierte JavaScript-Logik.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Event Loop Practice</title>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        /* Tailwind Extensions (Optional for Customizations) */
        body {
            font-family: 'Inter', sans-serif;
        }
    </style>
</head>
<body>



<h3>
  
  
  Why Use Fuse.js?
</h3>

<p>Fuse.js is a lightweight, customizable library for fuzzy searching. It handles typos and partial matches, offers high performance for large datasets, and has an intuitive API. This will help enhance your search functionality with flexible, user-friendly search experiences. Additionally, this provides you with a CDN link, so it can work right of the bat, no imports or local storage required.</p>

<h2>Now let's Code in the Real Deal - The JS</h2>

<h4>
  
  
  1. Task Array and Variables
</h4>



<pre class="brush:php;toolbar:false">const tasks = new Array (
    "Complete Blog on Throttling + Debouncing",
    "Make a list of 2025 Resolutions",
);
let fuse = undefined;
let debounceTimer;
let throttleTimer;
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Abschnitt wird eine Reihe von Aufgaben initialisiert und Variablen für Fuse.js, den Debounce-Timer und den Throttle-Timer deklariert. Wir haben einige Aufgaben bereits fest codiert – für dieses Projekt

Jetzt bauen wir die onSubmit-Funktion auf. Diese Funktion wird ausgelöst, sobald der Benutzer auf den Senden-Pfeil klickt. Es verhindert die standardmäßige Formularübermittlung, ruft den Eingabewert ab, löscht das Eingabefeld, fügt die neue Aufgabe zum Aufgabenarray hinzu und aktualisiert die Aufgabenliste.

let debounceTimer; // Timer reference

const handleOnPressKey = () => {
    console.log("Key pressed and debounce period elapsed!");
};

const debouncedKeyPress = () => {
    // Clear any existing timer
    clearTimeout(debounceTimer);

    // Start a new debounce timer
    debounceTimer = setTimeout(() => {
        handleOnPressKey(); // Execute the function after cooldown
    }, 2000); // Cooldown period of 2000ms
};

// Attach debouncedKeyPress to keypress events
document.getElementById("input").addEventListener("keypress", debouncedKeyPress);
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt müssen wir sicherstellen, dass eine einmal übermittelte Aufgabe in der Aufgabenliste aktualisiert wird

let throttleTimer; // Timer reference

const handleOnScroll = () => {
    console.log("Scroll event processed!");
};

const throttledScroll = () => {
    if (!throttleTimer) {
        handleOnScroll(); // Execute the function immediately
        throttleTimer = setTimeout(() => {
            throttleTimer = null; // Reset timer after cooldown
        }, 2000); // Cooldown period of 2000ms
    }
};

// Attach throttledScroll to scroll events
document.addEventListener("scroll", throttledScroll);
Nach dem Login kopieren
Nach dem Login kopieren

Die Funktion updateList() rendert die Aufgabenliste, indem sie das Aufgabenarray durchläuft und Listenelemente für jede Aufgabe erstellt. Jedes Listenelement enthält einen Aufzählungspunkt und den Aufgabentext.

Jetzt müssen wir sicherstellen, dass die Liste nach dem ersten Laden der Seite aktualisiert wird. Wir möchten auch Fuse.js beim Laden der Seite initialisieren – und das Aufgabenarray damit verknüpfen. Denken Sie daran, dass wir Vorschläge aus diesem Aufgabenarray im Dropdown-Menü darstellen möchten.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Event Loop Practice</title>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        /* Tailwind Extensions (Optional for Customizations) */
        body {
            font-family: 'Inter', sans-serif;
        }
    </style>
</head>
<body>



<h3>
  
  
  Why Use Fuse.js?
</h3>

<p>Fuse.js is a lightweight, customizable library for fuzzy searching. It handles typos and partial matches, offers high performance for large datasets, and has an intuitive API. This will help enhance your search functionality with flexible, user-friendly search experiences. Additionally, this provides you with a CDN link, so it can work right of the bat, no imports or local storage required.</p>

<h2>Now let's Code in the Real Deal - The JS</h2>

<h4>
  
  
  1. Task Array and Variables
</h4>



<pre class="brush:php;toolbar:false">const tasks = new Array (
    "Complete Blog on Throttling + Debouncing",
    "Make a list of 2025 Resolutions",
);
let fuse = undefined;
let debounceTimer;
let throttleTimer;
Nach dem Login kopieren
Nach dem Login kopieren
const onSubmit = (event) => {
    //Prevent default
    event.preventDefault();

    const text = document.getElementById("input").value.trim();
    document.getElementById("input").value = "";
    tasks.push(text);
    updateList();
}
Nach dem Login kopieren

Jetzt müssen wir sicherstellen, dass wir bei jeder „Eingabe“ die Liste durchsuchen, um Vorschläge im Dropdown anzuzeigen. Dies besteht aus 3 Teilen:

  • Schreiben Sie die Suchlogik: searchTasks()
  • Dropdown bei jeder Eingabe füllen: updateDropdown()
  • Verknüpfen Sie die updateDropdown(), die bei jeder Eingabe aufgerufen werden soll (zumindest für den Moment :-) -> Bis wir die Entprellungs-/Drosselungslogik implementieren)
const updateList = () => {
    const lists = document.getElementById("taskList");
    lists.innerHTML = "";

    //Loop through all elements in tasks
    tasks.forEach(task => {
        const taskElement = document.createElement("li");
        taskElement.classList.add("flex", "items-center", "space-x-2");

        //Add Bullet Point Element
        const bullet = document.createElement("span");
        bullet.classList.add("h-2", "w-2", "bg-blue-500", "rounded-full");

        //Add Span Tag
        const taskText = document.createElement("span");
        taskText.textContent = task;

        taskElement.appendChild(bullet);
        taskElement.appendChild(taskText);
        lists.appendChild(taskElement);
    })
}
Nach dem Login kopieren
const init = () => {
    console.log("Initializing...");
    //Update and render the list
    updateList();

    //Initialize Fuse with the updated array
    try{
        fuse = new Fuse(tasks, {
            includeScore: true,
            threshold: 0.3 //For sensitivity
        })
    } catch(e) {
        console.log("Error initializing Fuse:"+ fuse);
    }
}
Nach dem Login kopieren
document.addEventListener("DOMContentLoaded", init);
Nach dem Login kopieren

Bisher: Die Dropdown-Liste wird jedes Mal aktualisiert, wenn Sie etwas eingeben – in einer umfangreicheren Benutzeroberfläche würden wir dieses Erlebnis nicht wollen

Das Aktualisieren der Dropdown-Liste bei jedem Tastendruck in einer umfangreichen Benutzeroberfläche kann zu Leistungsproblemen führen, was zu Verzögerungen und einer schlechten Benutzererfahrung führt. Häufige Aktualisierungen können die Ereignisschleife überfordern und zu Verzögerungen bei der Verarbeitung anderer Aufgaben führen.

Wir werden nun sehen, wie wir mithilfe von Entprellen ODER Drosseln die Aktualisierungshäufigkeit verwalten und so eine reibungslosere Leistung und eine reaktionsfähigere Benutzeroberfläche gewährleisten können.

So können wir beide Techniken in unserem Notizenprojekt implementieren.

Entprellen:

Entprellen stellt sicher, dass eine Funktion erst aufgerufen wird, nachdem seit dem letzten Aufruf eine bestimmte Zeitspanne vergangen ist. Dies ist nützlich für Szenarien wie Sucheingabefelder, bei denen wir warten möchten, bis der Benutzer mit der Eingabe fertig ist, bevor wir einen API-Aufruf tätigen.

Code-Snippet:

//Utility function to search within already entered values
const searchTasks = (query) => {
    const result = fuse.search(query);
    const filteredTasks = result.map(result => result.item)
    updateDropdown(filteredTasks);
}
Nach dem Login kopieren

Erklärung:

  • Der Eingabeereignis-Listener ist an das Eingabefeld angehängt.
  • Die Funktion „clearTimeout“ löscht alle vorhandenen Entprell-Timer.
  • Die Funktion setTimeout setzt einen neuen Entprell-Timer für 1 Sekunde. Wird innerhalb dieses Zeitraums keine Eingabe erkannt, wird die Funktion searchTasks mit dem Eingabewert aufgerufen.

Drosselung (im selben Anwendungsfall) – Verwenden Sie einen der beiden Ansätze

const updateDropdown = (tasks) => {
    const dropdown = document.getElementById("dropdown");
    dropdown.innerHTML = "";

    if(tasks.length === 0) {
        dropdown.style.display = "none";
        return;
    }

    tasks.forEach(task => {
        const listItem = document.createElement("li");
        listItem.textContent = task;
        listItem.addEventListener("click", () => {
            document.getElementById("input").value = task;
            dropdown.style.display = "none";
        })
        dropdown.appendChild(listItem);
    });

    dropdown.style.display = "block";
}
Nach dem Login kopieren

Erklärung:

  • let lastCall = 0;: Initialisiert eine Variable, um den letzten Aufruf von searchTasks zu verfolgen.
  • document.getElementById("input").addEventListener("input", (event) => { ... });: Hängt einen Eingabeereignis-Listener an das Eingabefeld an.
  • const now = Date.now();: Ruft die aktuelle Zeit in Millisekunden ab.
  • const delay = 1000;: Setzt die Drosselverzögerung auf 1 Sekunde.
  • if (now - lastCall >= Verzögerung) { ... }: Prüft, ob seit dem letzten Anruf genügend Zeit vergangen ist.
    • const query = event.target.value.trim();: Ruft den gekürzten Eingabewert ab.
    • searchTasks(query);: Ruft die searchTasks-Funktion mit dem Eingabewert auf.
    • lastCall = now;: Aktualisiert die lastCall-Zeit auf die aktuelle Zeit.

Bitte beachten Sie jedoch: Drosselung ist für dieses Szenario nicht die beste Lösung, da sie die Häufigkeit der Funktionsausführung auf ein festes Intervall begrenzt, was möglicherweise nicht die beste Benutzererfahrung für Echtzeit-Suchvorschläge bietet. Benutzer erwarten während der Eingabe eine sofortige Rückmeldung, und die Drosselung kann zu spürbaren Verzögerungen führen.

Bessere Anwendungsfälle für die Drosselung

Drosselung eignet sich besser für Szenarien, in denen Sie die Geschwindigkeit der Ereignisverarbeitung steuern möchten, um Leistungsprobleme zu vermeiden. Hier einige Beispiele:

  • Fenstergröße ändern: Wenn ein Benutzer die Größe des Browserfensters ändert, möchten Sie möglicherweise das Layout aktualisieren oder Berechnungen durchführen. Durch die Drosselung wird sichergestellt, dass diese Aktualisierungen mit einer kontrollierten Geschwindigkeit erfolgen, wodurch übermäßige Funktionsaufrufe verhindert werden.
  • Scrollen: Bei der Verarbeitung von Scroll-Ereignissen, wie dem Laden weiterer Inhalte oder dem Aktualisieren der Benutzeroberfläche basierend auf der Scroll-Position, hilft die Drosselung dabei, die Aktualisierungshäufigkeit zu verwalten und so eine reibungslose Leistung sicherzustellen.
  • API-Ratenbegrenzung: Bei API-Aufrufen kann Ihnen die Drosselung dabei helfen, die Ratengrenzen einzuhalten, indem die Häufigkeit der Anfragen gesteuert wird.

Durch die Verwendung der Drosselung in diesen Szenarien können Sie die Leistung verbessern und ein reibungsloseres Benutzererlebnis gewährleisten.

Den vollständigen Code finden Sie hier

Viel Spaß beim Codieren!


Bitte hinterlassen Sie ein Feedback!

Ich hoffe, Sie fanden diesen Blog hilfreich! Ihr Feedback ist für mich von unschätzbarem Wert. Bitte hinterlassen Sie Ihre Gedanken und Vorschläge in den Kommentaren unten.

Vernetzen Sie sich gerne mit mir auf LinkedIn, um weitere Einblicke und Updates zu erhalten. Bleiben wir in Verbindung und lernen und wachsen wir gemeinsam weiter!

Das obige ist der detaillierte Inhalt vonDie Kunst von Smooth UX: Entprellen und Drosseln für eine leistungsfähigere Benutzeroberfläche. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage