ref(Vue3中的用法)

569 阅读3分钟

我正在参加「掘金·启航计划」

  前言: 之前 Vue2 的写法用得比较多,然后有次在写 Vue3 项目时碰到了 ref ,当时是完全不懂,所以决定在后面把它的用法做个总结。

一、Refs(响应式API)

  相信大家对于 Vue3 的 ref 响应式肯定很熟悉,对于基本数据类型,他还是同 Vue2 一样,是靠 Object.defineProperty()getset 方法完成的;但是对于对象类型的数据,则是利用 Vue3 中的一个新的函数—— reactive 函数。

1、常见用法:

let name = ref("张三");   //创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
  • JS中操作数据 name.value
  • 模板中读取数据 <div>{{ name }}</div>

2、unref

  接收一个参数,若参数为ref类型则返回内部的值,否则返回参数本身。用法类似于:val = isRef(val) ? val.value : val

let val = unref(val)

3、toRef

  用来为源响应式对象上的 property 新创建一个 ref。然后可以将 ref 传递出去,从而保持对其源 property 的响应式连接。将 prop 的 ref 传递给复合函数时,toRef 很有用。

props: ["person"],
setup(props) {
    let newPerson = toRef(props, "person");
}

4、toRefs

  将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref

const state = reactive({
  foo: 1,
  bar: 2
})
const stateAsRefs = toRefs(state)
state.foo++
console.log(stateAsRefs.foo.value) // 2
stateAsRefs.foo.value++
console.log(state.foo) // 3

注意: toRef 能将对象中的某个属性转换为 ref 类型,而 toRefs 是将整个对象转换为 ref 类型,并且转换和被转换的数据之间类似于浅拷贝,任何一方的改变都会影响另一方的改变。若想双方互不影响,可以直接使用 ref 创建一个新的数据并赋默认值。

5、isRef

  用来判断是否是 ref 类型的数据let flag = isRef(参数)

6、customRef

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

  • 使用 v-model 使用自定义 ref 实现 debounce 的示例:
<input v-model="text" />
function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

export default {
  setup() {
    return {
      text: useDebouncedRef('hello')
    }
  }
}

7、shallowRef

  对于基本类型的数据是响应式的,对象类型的数据则不是响应式。

<div>爱好:{{ hobby }}</div>
<div>姓名:{{ person.name}}</div>
<div>年龄:{{ person.age}}</div>
let hobby = shallowRef("游泳") //当hobby的值发生改变时,页面也会发生变化
let person = shallowRef({name: "张三", age: 18}) //当person对象内的数据发生改变时,页面不会发生变化

8、triggerRef

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

const shallow = shallowRef({
  greet: 'Hello, world'
})
// 第一次运行时记录一次 "Hello, world"
watchEffect(() => {
  console.log(shallow.value.greet)
})
shallow.value.greet = 'Hello, universe' // 这不会触发,因为 ref 是浅层的
triggerRef(shallow) // 记录 "Hello, universe"

二、ref在标签中的使用

  ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:

<!-- vm.$refs.p 会是 DOM 节点 -->
<p ref="p">hello</p>

<!-- vm.$refs.child 会是子组件实例 -->
<child-component ref="child"></child-component>

<!-- 当动态绑定时,我们可以将ref定义为回调函数,显式地传递元素或组件实例 -->
<child-component :ref="(el) => child = el"></child-component>

当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。

关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也是非响应式的,因此你不应该试图用它在模板中做数据绑定。

我遇到的问题:

<child-component ref="child"></child-component>
let child = ref(null)
child.value.getFun()  //不理解child上为何有getFun这个函数

原因:这其实就是 Vue2 中的 this.$refs.child.getFun() ,或 this.$refs["child"].getFun() ,
getFun()是child组件里的一个方法, 这里是在父组件中调用子组件的方法。