這幾天學習Vue的官網,看到 customRef 提供了一個例子,研究半天發現這是一個防抖函數,以爲挺好,因而把這個例子擴展了一下,能夠用於表單子控件和查詢子控件。html
v-model
基於 element-plus 封裝表單控件,同時也要封裝一下表單子控件,還有查詢控件。
因爲 el-input 這類的組件,把 value 封裝成了 v-model,因此沒法把組件的屬性直接設置給內部的 el-input。
必須在內部設置一個變量,而後作「屬性」 <==> 「變量」 的轉換。
這樣就比較麻煩,須要一個既優雅又實用的方式來解決。vue
查詢的防抖
查詢的時候,理想狀況是用戶輸入一個完整的查詢條件,而後自動去後端申請查詢,可是vue默認的響應形式是,輸入一個字符就會響應,若是當即去後端查詢的話,會形成浪費的狀況,另外用戶體驗也很差。
若是用change事件,那麼用戶輸入完畢,還得在其餘的地方點一下,比較麻煩。
因此須要一個簡單的方式,好比防抖功能來優化用戶體驗。html5
用 customRef (自定義的ref)設計get 和 set。react
想法挺好的,演示爲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) } } }) }
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 很像,只是須要設置組件的屬性和上下文。
靈活性欠佳,只是針對一個特定需求封裝的,沒有考慮更多的狀況。其餘狀況在寫個函數好了,函數要符合原子性,不要承擔太多的職責。
仍是要傳遞屬性和上下文,這個也沒啥辦法省略。
CheckBox又不支持延遲了。記得以前好用的。。。