ホームページ > ウェブフロントエンド > jsチュートリアル > スムーズ UX の技術: よりパフォーマンスの高い UI のためのデバウンスとスロットル

スムーズ UX の技術: よりパフォーマンスの高い UI のためのデバウンスとスロットル

DDD
リリース: 2025-01-01 02:05:09
オリジナル
898 人が閲覧しました

Github コード リポジトリ

ペースの速い世界では、私たちが行う仕事のほとんどは Web 上で行われ、高速です。シームレスでスムーズなユーザー エクスペリエンスを作成することがますます重要になります。消費者は、ラグや遅延がなく、高速に動作する UI を好みます。完璧に近いエクスペリエンスを達成することは可能ですが、困難です。イベントループについて聞いたことがありますか?

JavaScript におけるイベント ループは、コードの実行順序を管理し、プロセスを収集し、キューに入れられたサブタスクに命令を入れ、非同期操作を効率的に実行する基本的な概念です。イベント ループの仕組みを簡単に説明します。

  • 呼び出しスタック: すべての関数は呼び出されるとこのスタックに追加され、制御フローが関数から返されるとスタックからポップアウトされます
  • ヒープ: すべての変数とオブジェクトには、このヒープからメモリが割り当てられます
  • キュー: メッセージ/命令のリスト - 次々に実行されます

このイベント ループはコール スタックを継続的にチェックします。 JavaScript コードの実行は、コールスタックが空になるまで継続されます。

イベント処理は、JavaScript アプリケーションを構築する上で非常に重要な部分です。このようなアプリケーションでは、複数のイベントを UI コンポーネントに関連付ける必要がある場合があります。

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

想像してみてください...

UI には、スポーツの最新ニュースを表に入力するためのボタンがあります。ここで、次のことが必要になります。

  • ボタンをクリックします (「クリック」イベント ハンドラーをボタンに関連付けます。
  • API から結果を取得する
  • 出力 (Json) を解析して表示します

これら 3 つのプロセスは同期的に連鎖しています。ボタンを繰り返し押すと、複数の API 呼び出しが行われることになり、その結果、UI が数秒間ブロックされ、ユーザー エクスペリエンスが遅れているように見えます。

これは、デバウンスやスロットリングなどのアプローチに適した使用例です。一連の複雑なイベントをトリガーするこのようなイベントの場合、このような操作を使用して API を呼び出す回数を制限したり、一般的な意味でイベントを処理する速度を制限したりできます。

デバウンスとスロットルとは何ですか?

デバウンス: 最後のイベントから指定されたクールダウン期間が経過するまで関数の実行を延期します。

例:

handleOnPressKey() を 2 秒デバウンスすると、ユーザーがキーを押すのを 2 秒止めた場合にのみ実行されます。

シナリオ:

  • 最初のキー押下: 2000 ミリ秒のタイマーを開始して handleOnPressKey() を呼び出します。
  • 1000 ミリ秒以内にキーを押すと、 タイマーがリセットされます。この最後のキー押下からさらに 2000 ミリ秒待ちます。
  • 2000 ミリ秒間キーが押されませんでした: タイマーが完了し、handleOnPressKey() が呼び出されます。

コードスニペット:

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);
ログイン後にコピー
ログイン後にコピー

スロットリング: イベントが発生する頻度に関係なく、指定された期間内に関数が最大 1 回呼び出されることを保証します。

例:

2 秒間隔で handleOnScroll() を調整すると、その期間内にスクロール イベントが複数回トリガーされたとしても、関数は最大 2 秒に 1 回実行されます。

シナリオ:

  • 初期スクロール イベント: handleOnScroll() が呼び出され、2000 ミリ秒のクールダウンが開始されます。
  • 2000 ミリ秒以内の後続のスクロール イベント: クールダウン期間がアクティブであるため、これらは無視されます。
  • 2000 ミリ秒後のスクロール イベント: handleOnScroll() が再度呼び出されます。

コード例:

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);
ログイン後にコピー
ログイン後にコピー

さあ、何かを作ってみましょう

このプロジェクトは、イベント処理におけるデバウンススロットリングの概念を探求するように設計された最新のTo-Do リストアプリケーションです。リアルタイムのタスク追加、Fuse.js による検索機能、動的な提案のドロップダウンが特徴です。

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

より重要な script.js に進む前に、HTML コードを簡単に見てみましょう

素早いスタイリングのために TailwindCSS を使用しました。ここでドキュメントを確認できます Tailwind ドキュメント - 迅速なプロトタイプを作成するのに非常に役立ちます

  • ヘッダー: ヘッダーにはページのタイトルが含まれます。
  • 入力フィールド: Tailwind CSS でスタイル設定された、メモを追加するための入力フィールド。
  • 提案のドロップダウン: ユーザーが入力すると提案が表示される非表示のドロップダウンです。
  • 静的タスクリスト: 追加されたタスクを表示するリスト。
  • スクリプト: あいまい検索用の Fuse.js ライブラリとカスタム JavaScript ロジック用の script.js ファイルが含まれています。
<!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;
ログイン後にコピー
ログイン後にコピー

このセクションでは、タスクの配列を初期化し、Fuse.js、デバウンス タイマー、およびスロットル タイマーの変数を宣言します。このプロジェクトのために、すでにいくつかのタスクをハードコーディングしました

次に、onSubmit 関数を構築しましょう。この機能は、ユーザーが送信矢印をクリックするとトリガーされます。これにより、デフォルトのフォーム送信が防止され、入力値が取得され、入力フィールドがクリアされ、タスク配列に新しいタスクが追加され、タスク リストが更新されます。

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);
ログイン後にコピー
ログイン後にコピー

次に、タスクが送信されるとタスク リストで更新されるようにする必要があります

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);
ログイン後にコピー
ログイン後にコピー

updateList() 関数は、タスク配列をループし、各タスクのリスト項目を作成することにより、タスク リストをレンダリングします。各リスト項目には箇条書きとタスクのテキストが含まれます。

ここで、ページが初めてロードされた後にリストが更新されるようにする必要があります。また、ページの読み込み時に Fuse.js を初期化し、それにタスク配列を関連付けたいと考えています。ドロップダウン内のこのタスク配列からの提案をレンダリングする必要があることを覚えておいてください。

<!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;
ログイン後にコピー
ログイン後にコピー
const onSubmit = (event) => {
    //Prevent default
    event.preventDefault();

    const text = document.getElementById("input").value.trim();
    document.getElementById("input").value = "";
    tasks.push(text);
    updateList();
}
ログイン後にコピー

次に、すべての「入力」でリストを検索し、ドロップダウンに候補を表示するようにする必要があります。これには 3 つの部分があります:

  • 検索ロジックを作成します: searchTasks()
  • すべての入力でドロップダウンを作成します: updateDropdown()
  • すべての入力で呼び出される updateDropdown() を関連付けます (少なくとも現時点では :-) ->デバウンス/スロットル ロジックを実装するまで)
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);
    })
}
ログイン後にコピー
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);
    }
}
ログイン後にコピー
document.addEventListener("DOMContentLoaded", init);
ログイン後にコピー

これまでのところ: ドロップダウン リストは、何かを入力するたびに更新されます。よりかさばる UI では、このエクスペリエンスは望ましくないでしょう

かさばる UI でキーストロークごとにドロップダウン リストを更新すると、パフォーマンスの問題が発生し、遅延やユーザー エクスペリエンスの低下が発生する可能性があります。更新を頻繁に行うとイベント ループに負荷がかかり、他のタスクの処理に遅れが生じる可能性があります。

ここでは、デバウンシングまたはスロットリングを使用して更新頻度を管理し、よりスムーズなパフォーマンスとより応答性の高いインターフェイスを確保する方法を見ていきます。

メモ作成プロジェクトでいずれかのテクニックを実装する方法を次に示します。

デバウンス:

デバウンスにより、最後の呼び出しから指定された時間が経過した後にのみ関数が呼び出されるようになります。これは、検索入力フィールドなど、ユーザーが入力を完了するのを待ってから API 呼び出しを行う場合に便利です。

コードスニペット:

//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);
}
ログイン後にコピー

説明:

  • 入力イベント リスナーは入力フィールドにアタッチされます。
  • clearTimeout 関数は、既存のデバウンス タイマーをクリアします。
  • setTimeout 関数は、新しいデバウンス タイマーを 1 秒に設定します。この期間内に入力が検出されなかった場合、入力値を使用して searchTasks 関数が呼び出されます。

スロットリング (同じ使用例) - 2 つのアプローチのいずれかを使用します

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";
}
ログイン後にコピー

説明:

  • let lastCall = 0;: 最後に searchTasks が呼び出された時刻を追跡する変数を初期化します。
  • document.getElementById("input").addEventListener("input", (event) => { ... });: 入力イベント リスナーを入力フィールドにアタッチします。
  • const now = Date.now();: 現在時刻をミリ秒単位で取得します。
  • const late = 1000;: スロットル遅延を 1 秒に設定します。
  • if (now - lastCall >=遅延) { ... }: 最後の呼び出しから十分な時間が経過したかどうかを確認します。
    • const query =event.target.value.trim();: トリミングされた入力値を取得します。
    • searchTasks(query);: 入力値を使用して searchTasks 関数を呼び出します。
    • lastCall = now;: lastCall 時刻を現在時刻に更新します。

ただし、注意してください: スロットリングは関数の実行頻度を一定の間隔に制限するため、このシナリオには最適ではありません。これにより、リアルタイムの検索候補に対する最適なユーザー エクスペリエンスが提供されない可能性があります。ユーザーは入力時にすぐにフィードバックが得られることを期待しており、スロットリングにより顕著な遅延が発生する可能性があります。

スロットルのより良い使用例

スロットリングは、パフォーマンスの問題を回避するためにイベント処理の速度を制御するシナリオに適しています。以下にいくつかの例を示します:

  • ウィンドウのサイズ変更: ユーザーがブラウザ ウィンドウのサイズを変更するとき、レイアウトを更新したり、計算を実行したりすることが必要になる場合があります。スロットリングにより、これらの更新が制御された速度で行われるようになり、過剰な関数呼び出しが防止されます。
  • スクロール: コンテンツの追加読み込みやスクロール位置に基づいた UI の更新などのスクロール イベントを処理する場合、スロットリングは更新頻度の管理に役立ち、スムーズなパフォーマンスを確保します。
  • API レート制限: API 呼び出しを行うとき、スロットリングによりリクエストの頻度を制御することで、レート制限内に保つことができます。

これらのシナリオでスロットリングを使用すると、パフォーマンスが向上し、よりスムーズなユーザー エクスペリエンスを確保できます。

完全なコードはここで見つけてください

コーディングを楽しんでください!


フィードバックを残してください!

このブログがお役に立てば幸いです!皆様からのフィードバックは私にとって非常に貴重ですので、ご意見やご提案を以下のコメント欄に残してください。

さらに詳しい情報や最新情報が必要な場合は、LinkedIn でお気軽にご連絡ください。つながりを保ち、一緒に学び、成長し続けましょう!

以上がスムーズ UX の技術: よりパフォーマンスの高い UI のためのデバウンスとスロットルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート