Vue3中ref的前世今生

582 阅读3分钟

一. ref、toRef、toRefs推荐用法

  1. setup里合成函数返回响应式对象时,使用toRefs(直接解构reactive对象会失去响应式)
  2. reactive实现对象的响应式,ref实现值类型响应式 (不是非要这样用,主要是为了定义更清晰)
  1. setup中返回toRefs(reactive对象)
  2. ref变量命名使用变量名+Ref(项目中总结,这样能知道.value是否ref返回)

二.为什么需要ref?

  1. 值类型会丢失响应式,所以用ref来定义值类型使其成为响应式,区分没有响应式的普通变量
  2. 对于第1点有同学会说了让值类型成为响应式用reactive也可以实现为啥还要有ref,但比如在setup、computed里都有可能返回值类型,需要响应式,如下列场景我们会经常遇到:
   const state = reactive({
            count: 20,
            name: '金卡'
        })

   // computed 返回的是一个类似于 ref 的对象,也有 .value
   const countNew = computed(() => {
     return state.count + 1
   })

通过上述代码可以看到,如果使用reactive来赋予值类型响应式,仍然会遇到很多场景需要返回一个值类型,所以ref应运而生就是顺理成章的事情。

  1. Vue3不定义ref,框架使用者会自造ref,可能会造成代码混乱,不利于维护

三.ref为什么会有.value?

想必很多同学和我一样,通过ref定义的响应式变量在setup里使用是各种.value满天飞,写多了有点烦。但实际上vue3这样设计是有其原理合理性的,下面我就来解释这种用法的原因:

1.ref实际上是一个对象,这个对象通过属性value来存储值

2.ref通过.value的set和get来实现响应式

直白点说,也就是ref必须通过一个对象来实现响应式,而value就是这个对象的一个属性。下面我们通过一些模拟代码来具体解释一下以上2点。

首先我们使用值类型来尝试模拟ref的实现:(并非真实实现,只是关于.value部分的模拟)

    const refFun = (getter)=> {
      let ref = 0  // 这里ref为值类型
      setTimeout(() => {
        ref = getter()
      }, 1000);
      return ref
    }
    let a
    a =  refFun (() => 100)
        

我们执行上面代码,然后过1秒之后再执行a,发现在setTimeout这个异步中修改的的值,a并没有成功更新为100,仍然是初始值0。

接下来我们用对象也就是引用类型来尝试模拟ref的实现:

const refFun = (getter)=> {
  const ref = {
    value:0
  } // 这里ref为引用类型
  setTimeout(() => {
    ref.value = getter()
  }, 1000);
  return ref
}
let a = {}
a =  refFun (() => 100)

我们惊喜的发现,1秒后定义在对象里的value值,成功更新为100!

看到这想必同学们也明白了为啥ref会有.value了,一句话总结就是:ref运用了引用类型是浅拷贝的基本特性,来配合实现数据的响应式。