用 customRef 作一個防抖函數,支持 element 等UI庫。

這幾天學習Vue的官網,看到 customRef 提供了一個例子,研究半天發現這是一個防抖函數,以爲挺好,因而把這個例子擴展了一下,能夠用於表單子控件和查詢子控件。html

需求

  • v-model
    基於 element-plus 封裝表單控件,同時也要封裝一下表單子控件,還有查詢控件。
    因爲 el-input 這類的組件,把 value 封裝成了 v-model,因此沒法把組件的屬性直接設置給內部的 el-input。
    必須在內部設置一個變量,而後作「屬性」 <==> 「變量」 的轉換。
    這樣就比較麻煩,須要一個既優雅又實用的方式來解決。vue

  • 查詢的防抖
    查詢的時候,理想狀況是用戶輸入一個完整的查詢條件,而後自動去後端申請查詢,可是vue默認的響應形式是,輸入一個字符就會響應,若是當即去後端查詢的話,會形成浪費的狀況,另外用戶體驗也很差。
    若是用change事件,那麼用戶輸入完畢,還得在其餘的地方點一下,比較麻煩。
    因此須要一個簡單的方式,好比防抖功能來優化用戶體驗。html5

設計

用 customRef (自定義的ref)設計get 和 set。react

  • get:獲取組件屬性,返回給內部組件,好比 el-input。
  • set:用smit提交給父組件。
  • settimeout:實現防抖。

實施驗證

想法挺好的,演示爲0的時候也是好用的,可是把延遲設爲200的時候確出現問題,首先是 el-input 的字符顯示也一塊兒延遲了,另外只會顯示最後一個字符,中間的字符都被吃掉了。後端

這是怎麼回事?用html5的 input 試驗的時候是沒有問題的呀。函數

辦法重臂困難多,幾經修改以後終於好用了。學習

/**
 * 自定義的ref,實現屬性和內部變量的數據轉換
 * @param { reactive } props 組件的屬性
 * @param { object } context 組件的上下文
 * @param { number } delay 延遲刷新的時間,單位:毫秒,默認:0
 * @param { string } name 要對應的屬性名稱,默認:modelValue
 * @returns 自定義的ref
 */
export const debounceRef = (props, context, delay = 0, name = 'modelValue') => {
  let value = props[name]
  // 計時器
  let timeout
  // 是否輸入狀態。輸入時取 value;輸入完畢取 modelValue 屬性
  let isInput = false
  return customRef((track, trigger) => {
    return {
      get () {
        track()
        if (isInput) {
          return value
        } else {
          return props[name]
        }
      },
      set (newValue) {
        isInput = true
        value = newValue // 綁定值
        trigger() // 組件內部刷新模板
        clearTimeout(timeout) // 清掉上一次的計時
        timeout = setTimeout(() => {
          // 修改 modelValue 屬性
          context.emit(`update:${name}`, newValue) // 提交給父組件
          // 用於區分是哪一個組件觸發的事件。
          context.emit('my-change', newValue, props.controlId, props.colName)
          isInput = false
        }, delay)
      }
    }
  })
}
  • 參數
  1. props: 組件的屬性,便於屬性值變動的時候能夠得到最新值。
  2. context 組件的上下文,方便向父組件提交。
  3. name:v-model的名稱,默認是 modelValue。
  4. delay:延遲時間,默認是0。
  • value:內部變量,用於初始值和用戶輸入的時候的綁定。優化

  • let timeout:定時器,便於清掉以前的定時。設計

  • let isInput = false
    用戶的輸入狀態,若是用戶處於敲鍵盤的狀態,那麼獲取內部的 value 綁定到 el-inupt;
    若是用戶沒有敲鍵盤,那麼獲取父組件的屬性值,綁定到 el-inupt。code

爲啥要這麼設置呢?沒辦法,若是直接獲取組件的屬性值的話,那麼會出現延遲的狀況,若是獲取內部 value 的話,父組件的屬性變化的時候,內部 el-input 不會有變化,因此只好這麼折騰一下。

後面的就是常規操做了,get 裏面根據狀態獲取屬性和 value,set 裏面向父組件提交。

使用

setup (props, context) {
  const value = debounceRef(props, context)
  return {
    value
  }
}
<el-input v-model="value"></el-input>

基本上和普通的 ref 很像,只是須要設置組件的屬性和上下文。

  • 若是名稱不是默認的 modelValue 的話,須要傳遞名稱;
  • 若是須要延遲響應的話,須要設置延遲時間,默認是不延遲的。

缺點:

  • 靈活性欠佳,只是針對一個特定需求封裝的,沒有考慮更多的狀況。其餘狀況在寫個函數好了,函數要符合原子性,不要承擔太多的職責。

  • 仍是要傳遞屬性和上下文,這個也沒啥辦法省略。

  • CheckBox又不支持延遲了。記得以前好用的。。。

優勢:

  • 自我感受仍是比較優雅的。
相關文章
相關標籤/搜索