customRef的强大之处

customRef的强大之处

一、ref的局限性

vue 复制代码
<template>
  <h3>{{ title }}</h3>
  <input type="text" v-model="title">

</template>

<script setup>
import { ref } from 'vue';

const title = ref('IT知识一享')

</script>

这里ref做了什么?

  1. 当我们在模板中读取值的时候,vue就知道ref被用到了,所以它实现了依赖追踪得功能;
  2. 当我们修改值得时候,vue知道要重新运行所有依赖它的effect了,然后就变化了,所以它实现了触发更新的功能;

ref无法做到什么?

  • ref是官网给我写好的,所以我们只能使用它,如果想在读取或者写入的时候插入一些自定义的功能就不行了;
  • 例如对值进行防抖、节流;
  • 同步到localStorage
  • 检验值的合法性
  • 将值转换为其他格式等等

customRef的基本用法

上面说了一些ref无法做到的东西,而这些customRef都可以做到,custom是一个工厂函数,它接受一个函数作为参数,这个函数会返回也给对象,该对象必须包含get和set,所以customRef的基本结构如下

vue 复制代码
const tilte = customRef(() => {
  return {
    get() { },
    set() { },
  }
})
  • 那我们现在假设一个需求,例如我在输入框输入之后,1秒之后模板的值才会改变;那我们现在先理解一下,get什么时候调用,set什么时候调用;get是读取的时候调用,例如我们title被读取的时候,就会调用get,而set是当title被修改的时候就调用了
vue 复制代码
<script setup>
import { ref, customRef } from 'vue';

// const title = ref('IT知识一享')
let title = customRef(() => {
  return {
    get() {
      console.log('get');
    },
    set() {
      console.log('set')
    },
  }
})
</script>

这里我们一刷新,就会输出get,因为我们在模板中调用title了,所以在模板和input中都调用了,输出两次,没有毛病

这里我们一输入,就会输出set,因为输入框和模板中是双向绑定的,所以一改变就会生效;

  • 那我们知道get和set的使用了,我们尝试一下,来修改一下值看看
vue 复制代码
<script setup>
import { ref, customRef } from 'vue';

let initValue = 'IT知识一享'
let title = customRef(() => {
  return {
    get() {
      return initValue
    },
    set(value) {
      console.log('set', value)
      initValue = value
    },
  }
})
</script>

这样看似没有毛病,但是就是不同同步到模板中,控制台也证明了数值确实会改变;

  • 这个时候就需要来讲解一下custom最核心的两个函数了,track()和trigger();
  • track():这个函数必须在getter中调用,他用于记录依赖,用大白话说,这个函数告诉vue,这个值非常重要,你好对这个值持续关注,一旦发生了变化赶紧去更新;
  • trigger():这个函数必须在setter中调用,他是用于触发更新,用大白话说就是告诉vue,那个值真的已经变了;
vue 复制代码
<script setup>
import { ref, customRef, triggerRef } from 'vue';

let initValue = 'IT知识一享'
let title = customRef((track, trigger) => {
  return {
    get() {
      track()
      return initValue
    },
    set(value) {
      console.log('set', value)
      initValue = value
      trigger()
    },
  }
})
</script>

发现这个好像真的可以了;

  • 那我们好像可以这么干了,比如一秒之后再模板上更新
vue 复制代码
<script setup>
import { ref, customRef } from 'vue';

let initValue = 'IT知识一享'
let title = customRef((track, trigger) => {
  return {
    get() {
      track()
      return initValue
    },
    set(value) {
      setTimeout(() => {
        console.log('set', value)
        initValue = value
        trigger()
      }, 1000)
    },
  }
})
</script>

这个确实可以实现了,但是这里还有一个问题,比如我们在输入框输入111222333,会发生什么呢?

这个问题的出现是因为一瞬间产生了很多的定时器,没有等这次结束,下一次又出来了,多个定时器堆积,一定会出现问题的。

  • 这里可以使用一个简答的防抖技巧,这个生成错乱的原因就是渲染的太快了原因,我们可以定义了变量,把定时器给存储到变量中,然后每次调用set的时候,也就是读取值得时候,都会清除上一个定时器,所以我们每次按键都会取消上一个定时器,我们只会保存目前得最后一个定时器,只执行最后一次
vue 复制代码
<script setup>
import { ref, customRef } from 'vue';

let initValue = 'IT知识一享'
let timer
let title = customRef((track, trigger) => {
  return {
    get() {
      track()
      return initValue
    },
    set(value) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        console.log('set', value)
        initValue = value
        trigger()
      }, 1000)
    },
  }
})
</script>
  • 如果仅仅是这样来使用customRef的话就太简单了,例如,现在你同事和你一样有同样的需求,那他是不是要和你一样,写这么一大堆的逻辑?完全没有必须要,我们可以制作一个hooks,给同事快速使用这个延迟的效果;
vue 复制代码
//新建一个文件useTitleRef.ts
import { customRef } from "vue"
export default function (initValue: any, delay: number) {
    let timer: number
    let title = customRef((track, trigger) => {
        return {
            get() {
                track()
                return initValue
            },
            set(value) {
                clearTimeout(timer)
                timer = setTimeout(() => {
                    console.log('set', value)
                    initValue = value
                    trigger()
                }, delay)
            },
        }
    })
    return { title }
}

我们通过暴露一个函数,而且有两个自定义的值,就是输入值和延迟的时间,然后返回一个title的对象给同事使用;

  • 然后我么只需要把这个hooks拿来用就可以了
vue 复制代码
import useTitleRef from './useTitleRef';

let { title } = useTitleRef('ITShare', 2000)

所以这里的逻辑可以实现很多很多中:常见的有

  1. 比如在搜索框输入时,我们不需要用户频繁请求,过一段时间再发送请求的逻辑;
  2. 比如我们希望一个响应式变量能够自动的和localStorage同步,刷新页面值依然保留;
  3. 比如我们制作一个计数器,但是需要限制范围,只能0-100;
  4. 上面的例子就是一些懒加载的案例,一些ref需要被延迟加载的;

注意:对于一些简单的逻辑,我们不应该杀猪用牛刀,普通的ref+watch+computed更加的好用清晰,customRef只适合需要封装get/set行为并对外提供接口的场景

相关推荐
柒.梧.2 小时前
深入理解AQS:Java并发编程的核心基石
java·开发语言
磊 子2 小时前
类和对象—>析构+拷贝+运算符重载
开发语言·c++·算法
小茴香3532 小时前
拖拽实现(原生JS+Vue)
前端·javascript·vue.js·typescript
whuhewei2 小时前
CSS文字外描边
前端·css
清风徐来QCQ2 小时前
js中的常用api
开发语言·javascript·ecmascript
风之舞_yjf2 小时前
Vue基础(30)_mixins配置项
前端·vue.js
leo__5202 小时前
基于Matlab和CPLEX的2变量机组组合调度程序
开发语言·matlab
csbysj20202 小时前
CSS 伪类详解
开发语言
Reisentyan2 小时前
[backend]GoLang Learn Data Day 2
开发语言·后端·golang