js節流の手ぶれ防止の応用シーンと、Vueでの節流の手ぶれ防止の具体的な実現操作


物語の背景:
入力ボックスを検索してドロップダウンデータを表示する必要がありますが、文字を入力して検索する必要はありませんので、入力終了200ミリ秒後に検索を行います。jsの流れを引いて、手ぶれを防ぐために、ネット上で既製のものを探していますが、多くは間違っていますので、自分でやりました。
まず概念を見ます
関数ジッパー(debounce):
イベントがトリガされたn秒後に再度コールバックを実行します。もしこのn秒以内にまたトリガされたら、タイムアウトします。典型的なケースは入力検索です。入力が終わってからn秒で検索要求が行われます。n秒以内にまた入力された内容は再計算されます。
関数の流れ:
一つの単位時間に一度の関数しか触発できないと規定していますが、この単位時間内に複数回の関数を触発すると一回だけ有効になります。典型的なケースはマウスが絶えずクリックして触発し、n秒以内に何度もクリックすると一回だけ有効になります。
set Timeout内でthisが失効しました。
これは、setTimeout関数で呼び出されたコードが、所在関数と完全に分離された実行環境で実行されているため、これにより、thisはwindowオブジェクトを指しています。下の図を見てください。

断点を打って、Cosolieの下でthisを出力するのはWindowの対象で、この問題を解決してsetTimeout関数の外で1つのthat=thisを定義してもいいです。出力thatはやはりこのコンポーネントの対象です。
vueの中の実際のコードを見てください。
1.入力枠に最後の文字を入力して2秒後に実行する(手ぶれ防止:debounce):
html数:
<input type=“text”class=“input”v-model=“search Text”@keyup=“debounce”/>
js:

    debounce: function(){
      let that = this
      if(timer){
        clearTimeout(timer)
      }
      timer = setTimeout(function () {
        console.log('  ')
        timer = undefined;
      },2000)
    }
timerはdebounce関数の内部に置かず、文書全体で定義します。以下の通りです。

効果のデモンストレーションは次の通りです。1回のテキストを2秒入力してから、何度も入力しますか?それとも一回実行しますか?成功しました。

2秒以内に何度もクリックして、一回だけ有効になります。
html数:

js:

    throttle: function(){
      let that = this
      let now = +new Date();
      if(lastTime && lastTime - now < 2000){
        clearTimeout(timer)
      }
      timer = setTimeout(function () {
        console.log('  ')
        lastTime = +new Date()
      },200)
    }
lastTimeはtimerと同じで、文書の全体像を定義します。以下の通りです。

一回目はクリックして出力し、二回目はダブルクリックして選択し、もう一度出力します。

補足知識:VUEの手ぶれと節流を防ぐための最良の解決策――関数式コンポーネント
前言
echartsの使用経験がある学生はこのような場面に遭遇したことがあるかもしれません。window.onresizeイベントのリピーターでecharts Box.resize()方法をトリガーしてリメイクすることができます。これはresizeイベントが連続的にトリガされることを意味します。もう一つのよくあるシーンはinputタグのinputイベントでバックエンドインターフェースを要求します。inputイベントも連続的にトリガします。もし私が「12」を入力したら、2回のインターフェースパラメータを要求します。それぞれは「1」と「12」です。無駄なネットワークリソースよりももっと大変なのはパラメータが「1」なら、データを返す時間がパラメータが「12」のインターフェースより遅くなります。私たちが得たデータは期待に合わないです。もちろんaxiosに基づいて多くのパッケージを作ることができます。前の要求をキャンセルしたり、ブロックで処理することができますが、やはり手ぶれ防止から始めるのが簡単です。
手ぶれや節流を防ぐのは何ですか?
関数ジッパー(debounce)
あるイベントが継続的にトリガされると、一定時間間隔でイベントが再起動されない場合、イベントハンドラ関数は一回だけ実行されます。設定された時間間隔が到来する前に、もう一度イベントがトリガされたら、再度遅延を開始します。
ケース:scrollイベントを継続的にトリガすると、すぐにhandle関数を実行しません。1000ミリ秒以内にscrollイベントをトリガしない場合、遅延は一回のhandle関数をトリガします。

function debounce(fn, wait) {
 let timeout = null
 return function() {
  if(timeout !== null) clearTimeout(timeout)   
  timeout = setTimeout(fn, wait);
 }
}
function handle() {  
 console.log(Math.random())
}
window.addEventListener('scroll', debounce(handle, 1000))
addEventListenerの2番目のパラメータは実はdebounce関数でreturn回の方法であり、let timeout=nullこの行コードはaddEventListenerの時だけ実行されました。イベントをトリガするたびに、前回の遅延器をクリアしながら新しい遅延器を記録します。scrollイベントが停止されてトリガーされた後、最後のレコードの遅延器はクリアされず、遅延して実行されます。これはdebounce関数の原理です。
関数の流れ
イベントを継続的にトリガすると、規則的な時間間隔ごとにイベント処理関数が実行される。
ケース:scrollイベントを継続的にトリガした場合、直ちにhandle関数を実行しません。1000ミリ秒ごとにhandle関数を実行します。

function throttle(fn, delay) { 
 var prev = Date.now()     
 return function() {        
  var now = Date.now()        
  if (now - prev > delay) {          
   fn()        
   prev = Date.now()       
  }     
 }    
}    
function handle() {      
 console.log(Math.random())   
}
window.addEventListener('scroll', throttle(handle, 1000))
fn関数を実行するたびにprevを更新して今回の実行時間を記録し、次のイベントのトリガ時に時間間隔が予め設定されているかどうかを判定し、上記の操作を繰り返す。
手ブレや節流は、mousemoove、scroll、resize、inputなどのイベントに使用されますが、その違いは、手ぶれ防止は連続的なイベント周期の終了時に一回だけ実行されます。また、イベント周期内では、間隔時間によって規則的に何回も実行されます。
vueでの実践
vueでの手ぶれ防止は以下の2つの方法にほかならない。
パッケージツール
パッケージ
パッケージツール
上のケースを改造すれば簡単なツールをパッケージ化できます。
utils.js

let timeout = null
function debounce(fn, wait) {
 if(timeout !== null) clearTimeout(timeout)
 timeout = setTimeout(fn, wait)
}
export default debounce
app.js

<input type="text" @input="debounceInput($event)">
 
import debounce from './utils'
export default {
 methods: {
  debounceInput(E){
   debounce(() => {
    console.log(E.target.value)
   }, 1000)
  }
 }
}
パッケージ
コンポーネントのパッケージについては、私たちは$listeners、$atrsの2つのプロパティを使用して、彼らはvue 2.4の新規コンテンツです。公式サイトの紹介は比較的に難しいです。彼らは一体何をしているのかを見に来ました。
listeners:親コンポーネントはサブアセンブリをバインディングする際に、多くの属性をサブアセンブリに結びつけられ、サブアセンブリにpropsで登録して使用すると、プロpsに登録されていないものは$listenersに入れられます。もちろん、classとstyleは含まれません。そしてv-bind="atrs"を通じてサブアセンブリの内部コンポーネントに入ることができます。
$listeners:親コンポーネントがサブアセンブリに結合されています。native修飾器を含まないイベントは、v-on="listeners"を通じて内部コンポーネントに入ることができます。
簡単に言えば、二人は属性とイベントの引き継ぎをしています。これはコンポーネントの二次パッケージを作る時にとても役に立ちます。
私達はelement-uiのel-inputコンポーネントを例にとって、震え防止のdeboune-inputコンポーネントを包装します。
debounce-input.vue

<template>
 <el-input v-bind="$attrs" @input="debounceInput"/>
</template>
<script>
export default {
 data() {
  return {
   timeout: null
  }
 },
 methods: {
  debounceInput(value){
   if(this.timeout !== null) clearTimeout(this.timeout)   
   this.timeout = setTimeout(() => {
    this.$emit('input', value)
   }, 1000)
  }
 }
}
</script>
ap.vue

<template>
 <debounce-input placeholder="  " prefix-icon="el-icon-search" @input="inputEve"></debounce-input>
</template>
<script>
import debounceInput from './debounce-input'
export default {
 methods: {
  inputEve(value){
   console.log(value)
  }
 },
 components: {
  debounceInput
 }
}
</script>
上のパッケージは$atrsを使っています。開発者が属性の伝達に関心を持つ必要はありませんが、使用上はやはり不便です。react高次のコンポーネントに接触したことがある学生は、react高次のコンポーネントは本質的には、パッケージを通して入ってきたReactコンポーネントであり、一連の処理を経て、最終的には比較的強化されたReactコンポーネントに戻ります。vueではこのような考えを参考にしてもいいですか?私達はvueの関数的なコンポーネントを調べてみます。
vue関数式コンポーネントについて
関数式コンポーネントは何ですか?
関数コンポーネントとは、一つのFunctionでvueコンポーネントをレンダリングすることを意味し、このコンポーネントはいくつかのpropを受け入れるだけで、これらのコンポーネントをfunctionalとしてマークすることができます。これは無状態(応答式データがない)を意味します。
関数コンポーネントは大体次のようになります。

export default () => {
 functional: true, 
 props: { 
  // Props     
 },
 //          ,             
 render: function (createElement, context) {
  return vNode
 }
}
注:2.3.0以前のバージョンでは、関数コンポーネントがpropを受信したい場合、propsオプションが必要です。2.3.0以上のバージョンでは、propsオプションを省略してもいいです。すべてのコンポーネントの特性は自動的に暗黙的にpropに解析されます。しかし、一旦propを登録したら、登録されたpropだけがcontext.propに現れます。
render関数の2番目のパラメータcontextはコンテキストthisの代わりに使用されます。彼は次のフィールドを含むオブジェクトです。
props:すべてのプロの対象を提供します。
children:VNodeサブノードの配列
slaots:一つの関数は、すべてのスロットを含むオブジェクトを返します。
scoped Slots:(2.6.0+)入力された作用ドメインスロットのオブジェクトを露出します。通常のスロットも関数として露出します。
data:コンポーネントに渡す全体のデータオブジェクトは、createelementの2番目のパラメータとしてコンポーネントに導入されます。
parent:親のコンポーネントへの参照
listeners:(2.3.0+)は、現在のコンポーネントに登録されているすべての親コンポーネントのイベントモニターのオブジェクトを含んでいます。これはdata.onの別名です。
injections:(2.3.0+)injectオプションを使用すると、オブジェクトは注入すべき属性を含んでいる。
slaots APIの中に何がありますか?
slaotsはスロットで配布された内容にアクセスするために使用されます。各具体的な名前スロットは、それぞれの属性(例えば、v-slot:fooの内容はvm.$slaots.fooの中に見つかる)を有する。default属性は、具名スロットに含まれていないノード、またはv-slot:defaultのすべてを含む。
sloots()とチルドレンの比較
なぜ同時に必要なのかを知りたいです。sloots().defaultはチルドレンと似ているのではないですか?いくつかの場面では、このようにしていますが、サブノードを有する関数的なコンポーネントは、以下のようになっていますか?

<my-functional-component>
 <p v-slot:foo>
  first
 </p>
 <p>second</p>
</my-functional-component>
このコンポーネントに対して、チルドレンは2つの段落ラベルを与えますが、SLots().defaultは2番目の匿名段落ラベルだけを伝えます。childrenとsloots()も持っていますので、モジュールにスロット機構を感知させるか、それとも単にchildrenを渡すかを選択して、他のコンポーネントに処理してください。
関数式コンポーネントの使用シーン
a 1、a 2、a 3の3つのコンポーネントを導入したと仮定して、aコンポーネントの親コンポーネントは、type属性がtypeの値aコンポーネントによって、a 1、a 2、a 3のうちのそのコンポーネントを決定する。このようなシーンaコンポーネントは、関数的なコンポーネントを使用するのに非常に便利である。なぜ関数式のコンポーネントを使うのですか?レンダリングオーバーヘッドは低いです。関数式コンポーネントは関数だけです。
関数式コンポーネントで手ぶれ防止を実現します。
この手ぶれ防止モジュールのパッケージは、input、button、el-input、el-buttonの使用を同時にサポートしていますので、input類のコンポーネントであれば、inputイベントに対する手ぶれ防止処理を行います。

const debounce = (fun, delay = 500, before) => {
 let timer = null
 return (params) => {
  timer && window.clearTimeout(timer)
  before && before(params)
  timer = window.setTimeout(() => {
    // click  fun Function input  fun Array
   if (!Array.isArray(fun)) {
    fun = [fun]
   }
   for (let i in fun) {
    fun[i](params)
   }
   timer = null
  }, parseInt(delay))
 }
}
export default {
 name: 'Debounce',
 functional: true, //          functional                   
 render(createElement, context) {
  const before = context.props.before
  const time = context.props.time
  const vnodeList = context.slots().default
  if (vnodeList === undefined){
   console.warn('<debounce>          ')
   return null
  }
  const vnode = vnodeList[0] || null //        dom
  if (vnode.tag === 'input') {
   const defaultFun = vnode.data.on.input
   const debounceFun = debounce(defaultFun, time, before) //       
   vnode.data.on.input = debounceFun
  } else if (vnode.tag === 'button') {
   const defaultFun = vnode.data.on.click
   const debounceFun = debounce(defaultFun, time, before) //       
   vnode.data.on.click = debounceFun
  } else if (vnode.componentOptions && vnode.componentOptions.tag === 'el-input') {
   const defaultFun = vnode.componentOptions.listeners.input
   const debounceFun = debounce(defaultFun, time, before) //       
   vnode.componentOptions.listeners.input = debounceFun
  } else if (vnode.componentOptions && vnode.componentOptions.tag === 'el-button') {
   const defaultFun = vnode.componentOptions.listeners.click
   const debounceFun = debounce(defaultFun, time, before) //       
   vnode.componentOptions.listeners.click = debounceFun
  } else {
   console.warn('<debounce>                     el-button、el-input、button、input')
   return vnode
  }
  return vnode
 }
}

<template>
 <debounce time="300" :before="beforeFun">
  <input type="text" v-model="inpModel" @input="inputChange"/>
 </debounce>
</template>
 
<script>
import debounce from './debounce'
export default {
 data() {
  return {
   inpModel: 1
  }
 },
 methods: {
  inputChange(e){
   console.log(e.target.value, '  ')
  },
  beforeFun(e){
   console.log(e.target.value, '   ')
  }
 },
 components: {
  debounce
 }
}
</script>
原理も簡単です。vNodeでonの下のclick、inputイベントをブロックして、手ぶれ防止処理をします。これは使いやすいです。
カスタムコマンドdirective
私達は一つの問題を考えてみます。ファンクションパッケージの震える関節はvNodeを取得しています。カスタムコマンドでvNodeももらえますし、元のDomももらえます。このようにカスタマイズコマンドで処理するのがもっと便利です。。。。。。
以上のjs节流防震应用シーンと、vueでの節流防振の具体的な実現操作は小編が皆さんに共有している内容です。参考にしていただければと思います。どうぞよろしくお願いします。