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

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

01/ 基础类型的响应性 —— ref

在vue3里面,我们可以通过 reactive 来实现引用类型的响应性,那么基础类型的响应性如何来实现呢?

可能你会想到这样来实现:

const count = reactive({value: 0})
count.value += 1

这么做确实可以实现,而且也很像 ref 的使用方式,都是要 .value 嘛。那么 ref内部 是不是这么实现的呢?

我们先定义两个 ref 的实例并且打印看看。

  const refCount = ref(0) // 基础类型
    console.log('refCount ', refCount )
    const refObject = ref({ value: 0 }) // 引用类型
    console.log('refObject ', refObject )


看一下结果:

37.png

基础类型的 ref

38.png

引用类型的 ref

我们都知道 reactive 是通过 ES6 的 Proxy 来实现的,基础类型的 ref 显然和 Proxy 没啥关系,而引用类型的 ref 是先把原型变成 reactive, 然后再挂到 value 上面。这样看来,和我们的猜测不太一样呢,那么 ref 到底是如何实现的呢?我们可以看一下 ref 的源码。


02/ref 的源码

代码来自于 vue.global.js ,调整了一下先后顺序。

  function ref(value) {
      return createRef(value);
  }
  function createRef(rawValue, shallow = false) {
      if (isRef(rawValue)) {
          return rawValue;
      }
      return new RefImpl(rawValue, shallow);
  }
  class RefImpl {
      constructor(_rawValue, _shallow = false) {
          this._rawValue = _rawValue;
          this._shallow = _shallow;
          this.__v_isRef = true;
          this._value = _shallow ? _rawValue : convert(_rawValue); // 深层 ref or 浅层ref
      }
      get value() {
          track(toRaw(this), "get" /* GET */, 'value');
          return this._value;
      }
      set value(newVal) {
          if (hasChanged(toRaw(newVal), this._rawValue)) {
              this._rawValue = newVal;
              this._value = this._shallow ? newVal : convert(newVal);
              trigger(toRaw(this), "set" /* SET */, 'value', newVal);
          }
      }
  }
  const convert = (val) => isObject(val) ? reactive(val) : val;


  • ref 这是我们使用的函数,里面使用 createRef 来创建一个实例。
  • createRef 做一些基础判断,然后进入主题,正式创建ref。这里还可以创建 shallowRef。
  • RefImpl 这个才是主体,显然这是 ES6 的 class,constructor 是初始化函数,依据参数创建一个实例,并且设置实例的属性。这个和上面 ref 的打印结果也是可以对应上的。整个class的代码也是非常简单,设置几个“内部”属性,记录需要的数据,然后设置“外部”属性 value,通过setter、getter 实现对 value 的操作拦截,set 里面主要是 trigger 这个函数,由它调用模板的自动刷新的功能。
  • convert 很显然,判断一下参数是不是 object,如果是的话,变成 reactive 的形式。这个就可以解释,引用类型的 ref 是如何实现响应性的,明显是先变成 reactive,然后在挂到 value 上面(挂之前判断一下是不是浅层的)。

03/ ref 和 reactive 的关系

通过打印结果的对比以及分析源码可以发现:

  • 基础类型的 ref 和 reactive 没有任何关系。
  • 引用类型的 ref ,先把 object 变成 reactive ,即利用 reactive 来实现引用类型的响应性。

关系就是这样的,千万不要再混淆了。

04/ shallowRef

浅层响应式,只监听 .value 的变化,真简单类型的响应式。

function shallowRef(value) {
      return createRef(value, true); // true 浅层
  }


通过源码我们可以发现,在把引用类型挂到 value 之前,先判断一下是不是浅层的,如果是浅层的,并不会变成 reactive,而是直接把原来的对象挂在 value 上面,shallowRef 和 ref 的区别就在于这一点。

我们写几个实例看看效果:


 setup () {
     // 浅层的测试 
    // 基础类型
    const srefCount = shallowRef(0)
    console.log('refCount ', srefCount )
    // 引用类型
    const srefObject = shallowRef({ value: 0 })
    console.log('refObject ', srefObject )
    // 嵌套对象
    const srefObjectMore = shallowRef({ info: {a: 'jyk'} })
    console.log('shallowRef ', srefObjectMore )
    // reactive 的 shallowRef
    const ret = reactive({name: 'jyk'})
    const shallowRefRet = shallowRef(ret)
    console.log('shallowRefRet ', shallowRefRet )
    // ==================== 事件 ==================
    // 修改基础类型
    const setNumber = () => {
      srefCount.value = new Date().valueOf()
      console.log('srefCount ', srefCount )
    }
    // 修改引用类型的属性
    const setObjectProp = () => {
      srefObject.value.value = new Date().valueOf()
      console.log('srefObject ', srefObject )
    }
    // 修改引用类型的value
    const setObject = () => {
      srefObject.value = { value: new Date().valueOf() }
      console.log('srefObject ', srefObject )
    }
    // 修改嵌套引用类型的属性
    const setObjectMoreProp = () => {
      srefObjectMore.value.info.a = new Date().valueOf()
      console.log('srefObjectMore ', srefObjectMore )
    }
    // 修改嵌套引用类型的value
    const setObjectMore = () => {
      srefObjectMore.value = { qiantao: 1234567 }
      console.log('srefObjectMore ', srefObjectMore )
    }
    // 修改reactive 的浅层ref
    const setObjectreactive = () => {
      shallowRefRet.value.name = '浅层的reactive'
      console.log('shallowRefRet ', shallowRefRet )
    }
  }


看看结果:

39.png

浅层的ref

测试了一下响应性:

  • 基础类型 srefCount 有响应性;
  • 引用类型 srefObject 的属性没有响应性,但是直接修改 .value 是有响应性的。
  • 嵌套的引用类型 srefObjectMore ,属性和嵌套属性都是没有响应性的,但是直接修改 .value 是有响应性的。
  • reactive 套上 shallowRef ,然后修改 shallowRef.value.属性 = xxx ,也是可以响应的,所以浅层的ref 也不绝对,还要看内部结构。

05/ triggerRef

手动执行与 shallowRef 关联的任何效果。

官网的中文版里面写的很绕,其实就是 让 shallowRef 原本不具有响应性的部分,具有响应性。shallowRef 是浅层的,深层部分是没有响应性的,那么如果非得让这部分也具有响应性呢?这时候可以用 triggerRef 来实现。好吧,目前还没有想到有啥具体的应用场景,因为一般都直接简单粗暴的用 ref 或者 reactive 了,全都自带响应性。

测试了各种情况,发现 triggerRef 并不支持 shallowReactive,还以为能支持呢。(或许是我写的测试代码有问题吧,官网也没提 shallowReactive)

基于上面的例子,在适当的位置加上 triggerRef(xxx)就可以了。


 setup () {
    // 引用类型
    const srefObject = shallowRef({ value: 0 })
    // 嵌套对象
    const srefObjectMore = shallowRef({ value: {a: 'jyk'} })
    // reactive 的 shallowRef
    const ret = reactive({name: 'reactive'})
    const shallowRefRet = shallowRef(ret)
    // 浅层的reactive
    const myShallowReactive = shallowReactive({info:{name:'myShallowReactive'}})
    const setsRet = () => {
      myShallowReactive.info.name = new Date().valueOf()
      triggerRef(myShallowReactive)  // 修改后使用,不支持
   }
    // ==================== 事件 ==================
    // 修改引用类型的属性
    const setObjectProp = () => {
      srefObject.value.value = new Date().valueOf()
      triggerRef(srefObject) // 修改后使用
    }
    // 修改引用类型的value
    const setObject = () => {
      srefObject.value = { value: new Date().valueOf() }
      triggerRef(srefObject)
   }
    // 修改嵌套引用类型的属性
    const setObjectMoreProp = () => {
      srefObjectMore.value.value.a = new Date().valueOf()
      triggerRef(srefObjectMore)
  }
    // 修改嵌套引用类型的value
    const setObjectMore = () => {
      srefObjectMore.value.value = { value: new Date().valueOf() }
      triggerRef(srefObjectMore)
    }
    // 修改reactive 的浅层ref
    const setObjectreactive = () => {
      shallowRefRet.value.name = '浅层的reactive' + new Date().valueOf()
      triggerRef(shallowRefRet)
    }
    return {
      srefObject, // 引用类型
      srefObjectMore, // 嵌套引用类型
      shallowRefRet, // reactive 的浅层ref
      myShallowReactive, // 浅层的reactive
      setsRet, // 修改浅层的reactive
      setObjectProp, // 修改引用类型的属性
      setObject, // 修改引用类型的value
      setObjectMoreProp, // 修改嵌套引用类型的属性
      setObjectMore, // 修改嵌套引用类型的value
      setObjectreactive // 试一试reactive的浅层ref
    }
  }

深层部分,不使用 triggerRef 就不会刷新模板,使用了 triggerRef 就可以刷新模板。话说,为啥会有这个函数?

相关文章
|
10天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
38 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
39 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
33 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
42 1
|
4天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
35 1
|
14天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
45 1
vue学习第一章
|
2月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
32 1
|
2月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
39 1
vue学习第四章
|
2月前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
30 1
vue学习第7章(循环)

热门文章

最新文章