用 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又不支持延迟了。记得以前好用的。。。

优势:

  • 自我感受仍是比较优雅的。
相关文章
相关标签/搜索