欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue3利用customRef實現(xiàn)防抖

 更新時間:2023年10月23日 15:30:36   作者:子辰Web草廬  
防抖就是對于頻繁觸發(fā)的事件添加一個延時同時設(shè)定一個最小觸發(fā)間隔,防抖大家都學(xué)過,但是如何在?Vue3?里中將防抖做到極致呢,下面小編就來和大家詳細(xì)講講

防抖你一定學(xué)過,但是你知道如何在 Vue3 里中將防抖做到極致嗎?什么樣的防抖才算是極致呢?

大家好,我是渡一前端子辰老師,首先,讓我們回顧一下防抖的概念。

防抖就是對于頻繁觸發(fā)的事件添加一個延時同時設(shè)定一個最小觸發(fā)間隔,如果觸發(fā)間隔小于設(shè)定的間隔,則清除原來的定時,重新設(shè)定新的定時;如果觸發(fā)間隔大于設(shè)定間隔,則保留原來的定時,并設(shè)置新的定時;

防抖的結(jié)果就是頻繁的觸發(fā)轉(zhuǎn)變?yōu)橛|發(fā)一次。

接下來,讓我們看一個具體的業(yè)務(wù)需求。

在項目中,我們有一個輸入框,我們希望在用戶輸入后間隔一段時間再執(zhí)行指定的邏輯。同時,由于防抖需要在項目中多處使用,因此我們希望能夠盡可能方便地重復(fù)使用,并且代碼精簡。

你可以在這里停一下,自己去嘗試嘗試,子辰在這里等你一會。

思考

我們先來看一個簡單的效果:在輸入框中輸入文本,下方會顯示我們輸入的文本。

<template>
  <div class="container">
    <input v-model="text" />
    <p class="result">{{ text }}</p>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const text = ref('');
</script>

然后我們現(xiàn)在要加入防抖功能,這意味著數(shù)據(jù)的變化不能立即發(fā)生,而是要等待一小段時間后再發(fā)生變化。

因此不能再使用 v-model,因為 v-model 的雙向綁定是數(shù)據(jù)及時響應(yīng)變化的。

不過我們可以利用 v-model 的特性,將其拆分為 :value=""@input="" 的形式。

<template>
  <div class="container">
    <!-- @input 綁定一個 handleUpdate 函數(shù) -->
    <input :value="text" @input="handleUpdate" />
    <p class="result">{{ text }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const text = ref('');
const handleUpdate = (e) => {
  text.value = e.target.value
}
</script>

這種形式就是 v-model 的原始形式,效果同 v-model 是一致的。

改成這種寫法之后,我們就可以利用 handleUpdate 函數(shù)進行操作了。

因為這里的防抖無非就是讓數(shù)據(jù)的變化延遲執(zhí)行,所以我們可以這樣寫:

<script setup>
  import { ref } from 'vue';
  const text = ref('');
  // 我們在這里定義一個 timer
  let timer;
  const handleUpdate = (e) => {
    // 每一次執(zhí)行這個函數(shù)的時候我們將 timer 情況
    clearTimeout(timer)
    // 然后再執(zhí)行 setTimeout,比如說間隔 1000ms
    setTimeout(() => {
      text.value = e.target.value
    }, 1000)
  }
</script>

/**
 * @description: 防抖函數(shù)
 * @param {Function} func: 要延遲執(zhí)行的函數(shù)
 * @param {Number} delay: 延遲時間 ( 默認(rèn) 1000ms )
 */
export function debounce(func, delay = 1000) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.call(this, ...args);
    }, delay);
  };
}

這樣我們就封裝好了一個防抖函數(shù)。

現(xiàn)在讓我們在組件中使用它。

<script setup>
  import { ref } from 'vue';
  import { debounce } from './debounce';
  const text = ref('');
  const update = (e) => (text.value = e.target.value)
  // 將 update 和 延遲時間傳入進去
  // 當(dāng)數(shù)據(jù)變化的時候,運行的是防抖函數(shù)
  // 那么防抖函數(shù)會隔一段時間再去運行這個 update 函數(shù),去更新數(shù)據(jù)
  const handleUpdate = debounce(update, 1000)
</script>

現(xiàn)在運行一下,效果還是沒問題的。

但是現(xiàn)在看起來還是不夠舒服,而且 v-model 原本用得好好地卻被換成了 :value=""@input="" 的形式。

而且每次使用時還需要寫很多冗余代碼,這我可忍受不了。

但是要繼續(xù)優(yōu)化就需要考驗?zāi)芰α?,我們說過我們要做到極致的防抖!

那當(dāng)然要在最初的代碼模式上做防抖才算最完美。

首先讓我們觀察一下 const text = ref('') 得到了什么。我們在控制臺中輸出 text 看看。

通過輸出結(jié)果可以看出,text 得到了一個叫做 RefImpl 對象。

這個對象里有一個 value 屬性,當(dāng)你看到 value 屬性是 (...) 時,你應(yīng)該立刻明白它是通過Object.defineProperty 定義的,通過定義之后就會產(chǎn)生 get 和 set 兩個函數(shù)。

也就是說,ref 的源代碼看上去就像是 get 和 set 的結(jié)構(gòu)。

根據(jù)上面發(fā)現(xiàn),讓我們寫出 ref 方法的結(jié)構(gòu):

// ref 方法需要一個值,我們寫作 value
function ref(value) {
  // 它返回一個對象
  {
    // 對象中有一個 value 屬性
    // 是通過 Object.defineProperty 定義的
    // 定義里會有一個 get 和 set 方法
    get(){
      // get 方法返回 value
      return value
    },
    set(val){
      // set 方法給 value重新賦值
      value = val
    }
  }
}

// 大概就是這個樣子
// 但是不要忘記了 vue 是帶有響應(yīng)式的
// 而響應(yīng)式的原理是什么呢? 
// 就是在 get 的時候進行依賴收集,就是說誰用到了我這個數(shù)據(jù)
// 而 set 的時候叫做派發(fā)更新,表示通知使用數(shù)據(jù)的地方數(shù)據(jù)更新了
// 那么就應(yīng)該是如下的樣子

function ref(value) {
  {
    get(){
      // 依賴收集,我們稱為 track()
      return value
    },
    set(val){
      // 派發(fā)更新 trigger()
      value = val
    }
  }
}

那么這跟我們要做的防抖有什么關(guān)系呢?

再看一下:如果能夠延遲 set 的派發(fā)更新,也就是說將 set 放到一個防抖函數(shù)中延遲執(zhí)行,那么問題就解決了。

那是否意味著我們要重新寫一個 ref 呢?將其源代碼重新實現(xiàn)一遍?其實沒有必要。

因為 vue 提供了一個自定義 ref 入口:customRef

通過 customRef 可以自定義 get 和 set 方法。

那么通過 customRef 我們就看到了勝利的曙光了,讓我們利用它寫出自己的 ref。

實現(xiàn)

import { customRef } from "vue";
/**
 * @description: 防抖的 ref
 * @param {*} value: ref 的 value
 * @param {*} delay: 延遲時間,表示 value 的更新要在多少時間后觸發(fā) ( 默認(rèn)為 1000ms )
 */
export function debounceRef(value, delay = 1000) {
  let timer;
  // 我們這里利用 customRef 來自定義 ref
  return customRef(() => {
    // customRef 中返回一個對象,對象中包含 get 和 set 函數(shù)
    return {
      get() {
        // 依賴收集 track()
        return value;
      },
      set(val) {
        // 使用 setTimeout 實現(xiàn)防抖
        clearTimeout(timer);
        timer = setTimeout(() => {
          // 派發(fā)更新 trigger()
          value = val;
        }, delay);
      },
    };
  });
}

上面代碼中我們實現(xiàn)了自定義 ref,并實現(xiàn)了防抖功能。

但缺少最關(guān)鍵依賴收集和派發(fā)更新部分,我們沒有時間重新實現(xiàn)依賴收集源碼和派發(fā)更新源碼。

但是 vue 貼心的考慮到了這一點,它給我們傳入了兩個參數(shù),tracktrigger,分別表示依賴收集和派發(fā)更新,我們只要在合適的時候調(diào)用即可。

import { customRef } from "vue";

export function debounceRef(value, delay = 1000) {
  let timer;
  return customRef((track, trigger) => {
    // 獲得 track, trigger 函數(shù)
    return {
      get() {
        // 依賴收集 track()
        track();
        return value;
      },
      set(val) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          value = val;
          // 派發(fā)更新 trigger()
          trigger();
        }, delay);
      },
    };
  });
}

現(xiàn)在去使用一下,看看效果。

<template>
  <div class="container">
    <input v-model="text" />
    <p class="result">{{ text }}</p>
  </div>
</template>

<script setup>
// 在使用時,我們要引入自定義的 ref
import { debounceRef } from './debounceRef';
const text = debounceRef('');
</script>

我們輸出 text 到控制臺看看。

可以看出得到的是一個 CustomRefImpl 對象,這個 value 呢,也是一個 get 和 set,只不過現(xiàn)在用的是我們自己寫的 get 和 set。

來試一下效果。

總結(jié)

通過現(xiàn)在的代碼,我們可以輕松實現(xiàn)防抖的功能,而且使用時非常簡單,只需要一個 ref 就可以做到。

這種方式給我們帶來了很大的便利,讓我們在開發(fā)過程中更加高效。

以上就是vue3利用customRef實現(xiàn)防抖的詳細(xì)內(nèi)容,更多關(guān)于vue3 customRef防抖的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論