[深入vue3之refs] ref、unref、toRef、toRefs、isRef、customRef、shallowRef、triggerRef等使用与讲解

发布时间 2023-03-22 21:15:37作者: 中亿丰数字科技

ref

  1. 接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。
  2. 如果将对象分配为 ref 值,则它将被 reactive 函数处理为深层的响应式对象。
  3. template 内使用 ref 对象,会自动解包。
<template>
  <div>
    {{Redf}}
  </div>
</template>
<script lang='ts' setup>
import { ref,isReactive } from 'vue';
let Redf = ref('南中乱党')
console.log(Redf.value);

let objData = ref({})
console.log(isReactive(objData.value));
</script>

unref

  1. 如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。
  2. 基于 ref 一个 {} , {} 会被 reactive 二次处理,unref(ref({})) 返回的是响应式的 {}
<script lang='ts' setup>
import { ref } from 'vue';
let Redf = ref('南中乱党')
let un1 = unref({})
let un2 = unref(Redf)
let un3 = unref(ref({})) // un3 > proxy {}
</script>

toRef

  1. 参数: (源对象 , 源对象属性)
  2. 可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
  3. 用人话讲就是为 源响应式对象(toRef的第一个参数) 上的某个 property 新创建一个 ref
<script lang='ts' setup>
import { reactive,toRef } from 'vue';
//! 新的ref对象对源property建立响应式连接,修改其中一个,另一个也会同步!
const state = reactive({
  foo: 1,
  bar: 2
})
const fooRef = toRef(state, 'foo')
fooRef.value++
state.foo++
console.log(fooRef,state) // 3
</script>

toRefs

  1. 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
  2. 主要功能:当从组合式函数 (.js) 返回响应式对象时,用toRefs就可以在不丢失响应性的情况下对返回的对象进行解构/展开。
  3. toRef 是转单个, toRefs全转。
    原理:toRefs 会将 reactive 生成的对象的根级属性全都用 ref 转成 ref 对象,然后解构出来的都是 ref 对象,从而不丢失响应式
<script lang='ts' setup>
import { reactive,toRef } from 'vue';
const state = reactive({
  foo: 1,
  bar: 2
})
let a = {...state}
const stateAsRefs = toRefs(state)
// stateAsRefs 是一个普通对象,stateAsRefs.foo则是响应式对象,因此{...}解构才不会丢失响应式
let {bar} = stateAsRefs 
console.log(stateAsRefs)
console.log(stateAsRefs.foo.value) // 因为使用了 ref ,理所应当 .value。
console.log(a)
</script>

isRef

  1. 检查值是否为一个 ref 对象。
<script lang='ts' setup>
import { isRef } from 'vue';
// ()内例子是上面的。
console.log(isRef(Redf));
console.log(isRef(stateAsRefs.foo));
</script>

customRef

  1. 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
    自定义 ref 的get set,用于某些数据变更操作的额外功能封装,可以理解为computed 指令类似的功能之类的
<script lang='ts' setup>
import { useDebouncedRef } from 'vue';
function useDebouncedRef(value, delay = 200) {
  let timeout;
  return customRef((track, trigger) => {
    return {
      get() {
        return value
      },
      set(newValue) {
        // 此处可以做一系列操作进行数据处理。
        // 例子1:使用自定义 ref 通过 v-model 实现 debounce 的示例:
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}
let a23 = useDebouncedRef('hello')
</script>

shallowRef

  1. 如字面意思,浅响应式,如果传入的是基本类型跟 ref 没区别。
  2. 如果传入的是引用类型,.value值将不会是响应式的数据,ref的 value 属性则会是响应式的。
<script lang='ts' setup>
import { shallowRef,isReactive } from 'vue';
const foo = shallowRef({a:1})
// 改变 ref 的值是响应式的
foo.value = {b:2}
// 但是这个值不会被转换。
isReactive(foo.value) // false
</script>
备注: <script setup>语法糖时,动态组件最好就是用 shallowRef

triggerRef

  1. 手动执行与 shallowRef 关联的任何作用 (effect)。
  2. 简单讲就是配合 shallowRef 用的,并且 shallowRef 传入的是个引用类型。
<script lang='ts' setup>
import { shallowRef,watchEffect,triggerRef } from 'vue';
const shallow = shallowRef({
  greet: 'Hello, world'
})
watchEffect(() => {
  // 第一次运行时记录一次 "Hello, world"
  console.log(shallow.value.greet)
})
shallow.value.greet = 'Hello, universe'

// 首先 watchEffect 追踪的是响应式数据,shallowRef 是浅层的,  
// 所以当是{}的时候,无法触发 effect ,每次操作 shallow.value 的时候,则需要 triggerRef(shallow) 主动触发执行。  
// 记住,是每次操作,都需要 triggerRef() 一下
triggerRef(shallow)
</script>

作者:雍广飞