手拉手带你了解Vue3的 ref 和相关函数以及计算属性(三)

简介: 前不久的一篇文章:手拉手带你了解Vue3的reactive和相关函数 已经对reactive以及相关的几个函数,做了一个综合介绍,今天继续和大家深入学习。

11/ customRef

自定义一个ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并应返回一个带有 get 和 set 的对象。

如果上面那段没看懂的话,可以跳过。

简单的说,就是在 ref 原有的 set、get 的基础上,加入我们自己写的代码,以达到一定的目的。

话说,官网写的例子还真是…… 反正一开始我是没看懂,后来又反复看,又把代码敲出来运行,又查了一下“debounce”的意思。最后终于明白了,这是一个防抖(延迟响应)的代码。

一般用户在文本框里输入内容,立即就会响应,但是如果在查询功能里面的话就会有点小郁闷。用户输入个“1”,立即就去后端查询“1”, 然后用户又输入个“2”,立即又去后端查询“12”, 然后用户又输入个“3”,立即又去后端查询“123”。…… 这个响应是很快,但是有点“折腾”的嫌疑,那么能不能等用户把“123”都输入好了,再去后端查询呢?

官网的例子就是实现这样的功能的,我们把例子完善一下,就很明显了。


const useDebouncedRef = (value, delay = 200) => {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track() // vue内部的跟踪函数
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger() // vue内部的自动更新的函数。
        }, delay) // 延迟时间
      }
    }
  })
}
  setup () {
    const text = useDebouncedRef('hello', 1000) // 定义一个 v-model
    console.log('customRef', text)
    const update = () => {
      // 修改后延迟刷新
      text.value = '1111' + new Date().valueOf()
    }
    return {
      text,
      update
    }
  }
  customRef 对象:{{text}} <br><br>
  <input v-model="text" type="text">


  • get 没有改变,直接用原方法。
  • set 使用 setTimeout 实现延迟响应的功能,把 Vue 内部的 trigger() 放在 setTimeout 里面就好。

这样就可以了,延迟多长的时间可以自定义,这里是一秒。一秒内用户输入的内容,会一次性更新,而不是每输入一个字符就更新一次。

  • v-model="text" 可以作为 v-model 来使用。


12/ customRef 的 源码

我们再来看看 customRef 内部源码的实现方式。


  function customRef(factory) {
      return new CustomRefImpl(factory);
  }
  class CustomRefImpl {
      constructor(factory) {
          this.__v_isRef = true;
          const { get, set } = factory(() => track(this, "get" /* GET */, 'value'), () => trigger(this, "set" /* SET */, 'value'));
          this._get = get;
          this._set = set;
      }
      get value() {
          return this._get();
      }
      set value(newVal) {
          this._set(newVal);
      }
  }


很简单的是不是,就是先这样,然后在那样,最后就搞定了。好吧,就是把 factory参数解构出来,分成set和get,做成内部函数,然后在内部的set和get里面调用。


13/ 自定义 ref 的实例 —— 写一个自己的计算属性。

一提到计算属性,我们会想到 Vue 提供的 computed,那么如果让我们使用自定义ref 来实现计算属性的功能的话,要如何实现呢?(注意:只是练习用)

我们可以这样来实现:


const myComputed = (_get, _set) => {
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        if (typeof _get === 'function') {
          return _get()
        } else {
          console.warn(`没有设置 get 方法`)
        }
      },
      set(newValue) {
        if (typeof _set === 'function') {
          _set(newValue)
          trigger()
        } else {
          console.warn(`没有设置 set 方法`)
        }
      }
    }
  })
}
setup () {
    const refCount = ref(0)
    const myCom = myComputed(() => refCount.value + 1)
    // const myCom = myComputed(() => refCount.value, (val) => { refCount.value = val})
    const update = () => {
      // 修改原型
      refCount.value = 3
    }
    const setRef = () => {
      // 直接赋值
      refCount.value += 1
    }
    return {
      refCount, // 基础类型
      myCom, // 引用类型
      update, // 修改属性
      setRef // 直接设置
    }
  }


  <div>
      展示 自定义 的 customRef 实现计算属性 <br>
      ref 对象:{{refCount}} <br><br>
      自定义的计算属性 对象:{{myCom}} <br><br>
      <input v-model="myCom" type="text">
      <el-button @click="update" type="primary">修改属性el-button><br><br>
    div>


  • myComputed 首先定义一个函数,接收两个参数,一个get,一个set。
  • customRef 返回一个 customRef 的实例,内部设置get 和 set。
  • 调用方法 调用的时候,可以只传入get函数,也可以传入get、set两个函数。修改 refCount.value 的时候,v-model 的 myCom 也会发生变化。
  • 实用性 那么这种方式有啥使用效果呢?在做组件的时候,组件的属性props是不能直接用在内部组件的 v-model 里面的,因为props只读,那么怎么办呢?

可以在组件内部设置一个ref,然后对props做监听,或者用computed来做。除了上面的几种方法外,也可以用这里的方法来实现,把 refCount 变成 props 的属性就可以了,然后set里面使用 smit 提交。


14/ computed

写完了自己的计算属性后,我们还是来看看 Vue 提供的计算属性。代码来自于 vue.global.js ,调整了一下先后顺序。


  function computed(getterOrOptions) {
      let getter;
      let setter;
      if (isFunction(getterOrOptions)) {
          getter = getterOrOptions;
          setter =  () => {
                  console.warn('Write operation failed: computed value is readonly');
              }
              ;
      }
      else {
          getter = getterOrOptions.get;
          setter = getterOrOptions.set;
      }
      return new ComputedRefImpl(getter, setter, isFunction(getterOrOptions) || !getterOrOptions.set);
  }
  class ComputedRefImpl {
      constructor(getter, _setter, isReadonly) {
          this._setter = _setter;
          this._dirty = true;
          this.__v_isRef = true;
          this.effect = effect(getter, {
              lazy: true,
              scheduler: () => {
                  if (!this._dirty) {
                      this._dirty = true;
                      trigger(toRaw(this), "set" /* SET */, 'value');
                  }
              }
          });
          this["__v_isReadonly" /* IS_READONLY */] = isReadonly;
      }
      get value() {
          if (this._dirty) {
              this._value = this.effect();
              this._dirty = false;
          }
          track(toRaw(this), "get" /* GET */, 'value');
          return this._value;
      }
      set value(newValue) {
          this._setter(newValue);
      }
  }


  • computed 暴露给我们用的方法,来定义一个计算属性。只有一个参数,可以是一个函数(function),也可以是一个对象。内部会做一个判断,然后做拆分。
  • ComputedRefImpl 是不是有点眼熟?这个是 ref 同款系列,都是 RefImpl 风格的,而且内部代码结构也很相似。这个是computed 的主体类,也是先定义内部属性,然后设置value的get和set。在get和set里面,调用外部设置的函数。

源码:

https://gitee.com/naturefw/nf-vue-cdn/tree/master/cdn/project-compositionapi

在线演示:

https://naturefw.gitee.io/nf-vue-cdn/cdn/project-compositionapi/

本文作者:自然框架

个人网址:jyk.cnblogs.com

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
13天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
25天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
26天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
29天前
|
API
vue3知识点:reactive对比ref
vue3知识点:reactive对比ref
28 3
|
29天前
|
JavaScript API
vue3知识点:ref函数
vue3知识点:ref函数
33 2
|
29天前
|
JavaScript API
vue3知识点:自定义hook函数
vue3知识点:自定义hook函数
26 2
|
29天前
|
API
vue3知识点:reactive函数
vue3知识点:reactive函数
28 1
|
7天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
vue学习第四章
|
7天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
vue学习第九章(v-model)
|
7天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
vue学习第十章(组件开发)
下一篇
无影云桌面