返回

从零出发实现 Vue3 中 shallowRef 的拦截功能

前端

一、初识 Vue3 中的 ref

在 Vue3 中,ref 是一个组合式 API,用于追踪基本数据类型的变化。通过调用 ref 函数,我们可以将一个普通变量转换为一个响应式变量,当该变量发生变化时,与之绑定的视图也会随之更新。

例如,我们可以使用 ref 来追踪一个文本框中的文本内容:

import { ref } from 'vue'

export default {
  setup() {
    const message = ref('')

    return {
      message
    }
  }
}

在模板中,我们可以使用 v-model 指令将文本框与 message 变量绑定:

<input v-model="message">

当用户在文本框中输入内容时,message 变量的值会发生变化,视图也会随之更新。

二、认识 shallowRef

shallowRef 是 Vue3 中另一个组合式 API,它是一种特殊的 ref,可以实现对对象或数组中个别属性的变化进行追踪。与 ref 不同的是,shallowRef 不会对整个对象或数组进行追踪,而是只追踪其指定属性的变化。

例如,我们可以使用 shallowRef 来追踪一个对象中 name 属性的变化:

import { shallowRef } from 'vue'

export default {
  setup() {
    const person = shallowRef({ name: '张三', age: 18 })

    return {
      person
    }
  }
}

在模板中,我们可以使用 v-model 指令将文本框与 person.name 变量绑定:

<input v-model="person.name">

当用户在文本框中输入内容时,person.name 属性的值会发生变化,视图也会随之更新。

三、手写实现 shallowRef 的拦截功能

shallowRef 的内部原理其实并不复杂,我们可以通过手写实现的方式来更好地理解其工作原理。

function shallowRef(value) {
  const proxy = new Proxy(value, {
    get(target, property) {
      // 当读取属性时,触发 getter
      return Reflect.get(target, property)
    },
    set(target, property, value) {
      // 当设置属性时,触发 setter
      const oldValue = Reflect.get(target, property)
      if (oldValue !== value) {
        // 如果属性值发生变化,则触发更新
        Reflect.set(target, property, value)
        // 通知视图更新
        trigger(target, property, value, oldValue)
      }
    }
  })

  return proxy
}

在上面的代码中,我们首先定义了一个 Proxy 实例,并将其作为 shallowRef 返回。这个 Proxy 实例将对原始值进行代理,并拦截对原始值属性的访问和修改。

当读取原始值属性时,触发 Proxy 实例的 getter 函数。该函数简单地将属性值返回。

当设置原始值属性时,触发 Proxy 实例的 setter 函数。该函数首先获取属性的旧值,然后检查新值是否与旧值不同。如果新值与旧值不同,则更新属性值并触发更新。

触发更新时,需要通知视图进行更新。我们可以通过调用 trigger 函数来实现这一点。trigger 函数会发出一个事件,通知视图更新与之绑定的数据。

四、结语

通过手写实现 shallowRef 的拦截功能,我们可以更好地理解 shallowRef 的工作原理。shallowRef 的拦截功能是基于 Proxy 对象实现的,通过对原始值属性的访问和修改进行拦截,我们可以实现对原始值属性变化的追踪。