[高頻度トリガーイベントの最適化] パッケージスロットリングとアンチシェイク

デジタル管理プラットフォーム
Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus
Vue パーミッションシステム事例
個人ブログアドレス

最近プロジェクトにデータエクスポート機能が追加されましたが、以前はバックエンドで同期処理、フロントエンドで手ぶれ補正処理を行っていたため、データ量が非常に多い場合、応答時間が非常に長くなってしまいました。今回は、バックエンドが最適化され、非同期で処理されます。応答は非常に高速ですが、エクスポートを処理するには追加のダウンロード ページが必要です。これにより、ユーザーがダウンロードを直接入力すると、ダウンロードされたファイルが表示されるまでしばらく待つことになります。バックエンドについては、手振れ補正で周波数を制御できますが (デフォルトは 1 秒)、2 回目のダウンロードを実現するために、時間を 10 秒程度に制御したいと考えています。手ぶれ補正を使い続ける場合、直接10秒を設定するとファーストクリックが無効になってしまいます もちろんこの時間を変数で動的に制御することも可能ですが、ちょっと面倒です。そこで私はスロットリングを使用して彼のニーズを実現しました。これは比較的簡単です。

アンチシェイク機能と機能スロットル機能は両方とも、特定の時間に頻繁にトリガーされるのを防ぎますが、この 2 つの兄弟の原理は異なります。どちらを使用するかは、実際の状況によって異なります。

[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-g14fT33a-1680157020919)(./%E9%A1%B9%E7%9B) %AE%E6%8F% 92%E4%BB%B6.assets/image-20230330101259343.png)]

関数スロットルと関数アンチシェイクは、DOM イベントを頻繁にトリガーする 2 つの一般的なソリューションです。

  1. **基本的な考え方:** 特定のコードは、中断せずに継続的に繰り返し実行することができません。
  2. 使用理由: DOM 操作には、非 DOM 操作よりも多くのメモリと CPU 時間が必要であり、あまりにも多くの DOM 操作を連続して試行すると、ブラウザがハングしたりクラッシュしたりする可能性があります。
  3. **アプリケーション シナリオ: **コードが定期的に実行される限り、調整する必要があります。マウスはmousemoveイベントを移動し、スクロールバーはscrollイベントをスクロールし、ブラウザウィンドウはサイズ変更イベントを変更します。
  4. 手ぶれ補正スロットルのロジック図

[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-z8CbQ2Ae-1680157020920)(./%E9%A1%B9%E7%9B) %AE%E6%8F% 92%E4%BB%B6.assets/%E9%98%B2%E6%8A%96%E8%8A%82%E6%B5%81.png)]

5.1 スロットル

スロットリング: 指定された時間内で 1 回だけトリガーされます。たとえば、500 ミリ秒の間隔を設定した場合、この期間内では、ボタンが何回クリックされても、トリガーされるのは 1 回だけです。

生活の例: 蛇口から水が滴り、一度に大量の水が滴る可能性がありますが、500 ミリ秒ごとにのみ滴下し、この頻度を維持したいと考えています。つまり、関数を許容可能な頻度で繰り返し呼び出す必要があります。

具体的なシナリオ: 1. 急いで商品を購入する際、無数の人が素早くボタンをクリックするため、クリックするたびにリクエストが送信されると、サーバーに多大な負荷がかかります。2. データエクスポート機能では、データ量が特に多い場合、一度クリックしたデータにバックエンドが応答するまでの処理に時間がかかり、エクスポートボタンを頻繁にクリックすると、サーバーが重大なクラッシュを引き起こす可能性があります。 。上記のシナリオが調整されると、リクエストの数が大幅に減少し、サーバーへの負荷が軽減されます。

コア: 関数が頻繁に実行されるのを防ぎ、一部の速すぎる操作を減らすためのものです。これは、Glory of Kings のスキルのクールダウン時間と同様です。

5.1.1 タイムスタンプを使用したスロットルのカプセル化

/**
 * @param {*} fn  需要包装的事件回调
 * @param {*} delay 每次推迟执行的等待时间
 */
export function throttle(fn, delay = 1000) {
  // 距离上一次的执行时间
  let latestTime = 0
  return function () {
    const _this = this
    const _arguments = arguments
    const nowTime = new Date().getTime()
    // 如果距离上次执行超过了,delay才能再次执行
    if (nowTime - latestTime > delay) {
      fn.apply(_this, _arguments)
      latestTime = nowTime
    } else {
      this.$message.warning(`下载频率过高,请${Math.ceil((delay - (nowTime - latestTime)) / 1000)}秒后再次下载!`)
    }
  }
}

タイムスタンプ バージョンが最初に実行され、1 回クリックするとすぐに実行されます。今回のプロジェクトエクスポート機能の最適化にも採用した手法です。

5.1.2 タイマーを使用したスロットルのカプセル化

export function throttle(fn, delay = 1000) {
  let timer = null
  return function () {
    const _this = this
    const _arguments = arguments
    if (!timer) {
      timer = setTimeout(function () {
        timer = null;
        fn.apply(_this, _arguments)
      }, delay)
    }
  }
}

タイマー バージョンは後で実行され、クリックは実行までの遅延時間まで待つ必要があります。以前やった手ぶれ補正はこんな感じで、実行後の場合は待ち時間を長く設定するのは良くありません。

5.2 手ぶれ補正(デバウンス)

手ぶれ補正:連続動作時は、どれだけ時間がかかっても、ある操作を行った後、規定時間内に次の操作がなかった場合に限り、その時間を有効と判断します。

生活の例: バネを押し、圧力をかけ続け、押し続けます。手を放す最後の瞬間にのみ反発します。つまり、関数が 1 回だけ呼び出されることを望みます。その前に関数が繰り返し呼び出されたとしても、最終的には 1 回だけ呼び出されます。

特定のシナリオ: 検索ボックスにキーワードを入力する処理中に、サーバーはリアルタイムで検索結果との照合を要求され、処理が実行されない場合、入力ボックスの内容が変化し続けるため、常にリクエストが送信されることになります。手ぶれ補正処理を行うと、コンテンツを入力した後、一定時間(500msなど)入力がないとリクエストがトリガーされます。

Core : 同じイベントが短期間に大量にトリガーされた場合、コールバック関数は 1 回だけ実行されます。1 つのイベントを複数のイベントと間違えないようにしてください。

パッケージ手ぶれ補正機能

/**
 * @param {*} fn  需要包装的事件回调
 * @param {*} delay 每次推迟执行的等待时间
 */
export function debounce(fn, delay = 1000){
  // 定时器
  let timer = null
  // 将debounce处理结果当作函数返回
  return function() {
    // 保留调用时的 this 上下文
    const _this = this
    // 保留调用时传入的参数
    const _arguments = arguments
    // 每次事件被触发时,都去清除之前的旧定时器
    if (timer) {
      clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function() {
      fn.apply(_this, _arguments)
    }, delay)
  }
}

5.3 一般的な使用シナリオ

  • スクロール、mousemove、その他のイベントをリッスン - スロットリング (毎秒位置を計算)
  • ブラウザウィンドウのサイズ変更操作を監視 - 手ぶれ補正 (計算は 1 回だけ必要)
  • キーボードのテキスト入力の検証 - 手ぶれ防止 (テキストを連続入力した後に検証リクエストを送信し、一度検証するだけ)
  • フォーム送信 - 手ぶれ防止 (複数回のクリックが 1 回になります)
  • search 検索 Lenovo では、ユーザーが連続して値を入力している場合、手ぶれ補正を使用してリクエストのリソースを節約します。
  • ログイン、テキスト メッセージ、その他のボタンの送信により、ユーザーがクリックしすぎて複数のリクエストが送信され、手ぶれ補正が必要になるのを防ぎます。

ここでは、ウィンドウのサイズ変更変更イベントを例として、ネイティブ コードによるスロットル効果と手ぶれ補正効果を示します。

デバウンスとスロットルを使用しないコード

window.onresize = function() {  
  var div = document.getElementById('mydiv')
  div.style.height = div.offsetWidth + 'px'
  console.log('resize')
}

ウィンドウのサイズを変更し続けて、印刷結果を観察します。

明らかに、ユーザーがブラウザ ウィンドウをズームインおよびズームアウトし続けると、監視関数が継続的に呼び出されます。関数が「重すぎる」場合、つまり上記で説明したように、ブラウザへの負荷が非常に高くなります。変更を頻繁に行うとブラウザがクラッシュする可能性があります。

手ぶれ補正処理

function debounce(fn, delay = 1000) {  
    var timer = null;
    return function() {
        if(timer) {
        	clearTimeout(timer)
        }
        var _arguments = arguments
        var _this = this
        timer = setTimeout(function() {
            fn.call(_this, _arguments)
        }, delay)
  }
}

function resizeDiv() {  
    var div = document.getElementById('mydiv')
    div.style.height = div.offsetWidth + 'px'
    console.log('resize');
}

window.onresize = debounce(resizeDiv)

ただし、イベントをトリガーして と の比較を確認すると、その問題は解決されます。

手を離してからしか変化しないので、ちょっと唐突な気がするのですが、ドラッグすると同期できるのでしょうか?? ?

スロットリング

function resizeDiv(){
    var div = document.getElementById('myDiv')
    div.style.height = div.offsetWidth + "px";
}
function throttle(fn, delay = 1000){
    clearTimeout(fn.timer);
    fn.timer = setTimeout(x=>{
        fn.call();
    }, delay)
}
window.onresize = function(){
    throttle(resizeDiv)
}

処理頻度を制御すると、ブラウザが非常に短期間に複数の計算を実行することがなくなります。明らかに、関数スロットリングは、手ぶれ補正よりもウィンドウの高頻度の変更に適しています。

おすすめ

転載: blog.csdn.net/qq_39335404/article/details/129856783