Vue3如何给reactive对象赋值,试试reactiveRef

175 阅读4分钟

实现 reactiveRef 的方法

以下是实现 reactiveRef 的方法,满足你的需求:

import { ref, isRef } from 'vue';

function reactiveRef(initialValue) {
  const internalRef = ref(initialValue);

  return new Proxy(internalRef, {
    get(target, prop, receiver) {
      // 直接访问实例时返回 .value 的值
      if (prop === Symbol.toPrimitive || prop === 'valueOf') {
        return () => target.value;
      }
      if (prop === 'toString') {
        return () => target.value.toString();
      }

      // 访问 .value 属性时返回原始值
      if (prop === 'value') {
        return target.value;
      }

      // 如果当前内容是对象,则代理到内部
      const v = internalRef.value;
      if (v && typeof v === 'object') return v[prop]

      // 保留 ref 的其他属性(如 .effect)
      return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
      // 设置 .value 属性时修改原始值
      if (prop === 'value') {
        target.value = value;
        return true;
      }

      // 否则给内部对象赋值
      if (typeof internalRef.value === 'object' && internalRef.value !== null) {
        internalRef.value[prop] = value;
        return true
      }

      // 其他属性直接设置
      return Reflect.set(target, prop, value, receiver);
    },

    // 直接调用对象(如reactiveRef(1)())时返回internalRef.value
    apply(target, thisArg, argumentsList) {
      return internalRef.value
    }
  });
}

// 使用示例
const count = reactiveRef(0)
console.log(count.value) // 0
count.value = 10;
console.log(count.value) // 10

// 如果value默认是一个对象
const state = reactiveRef({a: 1, b: 2});
console.log(state.a) // 1
state.a = 12
console.log(state.a) // 12

// 依然可以用state.value = {...}替换整个对象
state.value = {a: 9, b: 9}
console.log(state.a)
console.log(state.value.b)

特性说明

  • 直接访问值:当直接访问 count 时,会自动返回 .value 的值
  • 强制使用 .value 设置:必须通过 count.value = newValue 修改值
  • 保留完整功能
    • 仍可通过 count.value 访问原始 ref 对象
    • 保留 isRef 检测等原始功能
    • 响应式系统正常工作

注意事项

  • 不要直接给 count 变量赋值(如 count = 5),这会改变变量指向的对象
  • 深层次对象仍需保持响应式(与普通 ref 行为一致)
  • 在模板中使用时仍会自动解包,与普通 ref 行为一致

这个实现通过 Proxy 拦截了属性的访问和设置,在保持 Vue 响应式系统完整性的同时,修改了值的访问方式。

---------------文章补充

  • reactiveRef 创建响应式变量后,访问属性和赋值都不需要 .value,直接用变量名即可操作属性。
  • 但在需要整体替换变量内容时,可以通过 .value 赋新值。

这类似于把 refreactive 的优点结合起来。你可以自定义如下的 reactiveRef 实现:

import { reactive, ref } from 'vue'

export function reactiveRef(initialValue) {
  const _ref = ref(initialValue)
  const proxy = reactive({
    get value() {
      return _ref.value
    },
    set value(val) {
      _ref.value = val
      // 如果你希望自动把新对象变成响应式,可以加下面这句
      // Object.assign(proxy, val)
    }
  })

  // 代理内部属性访问,实现不用.value即可访问
  return new Proxy(proxy, {
    get(target, key, receiver) {
      if (key === 'value') return target.value
      // 允许直接访问和设置属性
      return Reflect.get(target.value, key, receiver)
    },
    set(target, key, value, receiver) {
      if (key === 'value') {
        target.value = value
        return true
      }
      // 允许直接设置属性
      target.value[key] = value
      return true
    },
    ownKeys(target) {
      return Reflect.ownKeys(target.value)
    },
    getOwnPropertyDescriptor(target, key) {
      return Object.getOwnPropertyDescriptor(target.value, key)
    }
  })
}

用法示例

const state = reactiveRef({ name: 'Alice', age: 20 })

// 直接访问属性,无需 .value
console.log(state.name) // 'Alice'
state.age = 21
console.log(state.age) // 21

// 整体赋值用 .value
state.value = { name: 'Bob', age: 30 }
console.log(state.name) // 'Bob'

特点说明

  • 你可以像 reactive 一样直接用 state.xxx 访问和修改属性。
  • 你可以像 ref 一样用 state.value 赋值来整体替换对象。
  • 这样在模板和脚本中都很方便,减少了 .value 的繁琐操作。

下面对代码进行详细解释。这个 reactiveRef 函数的目的是结合 refreactive 的优点,实现如下效果:

  • 像操作 reactive 一样访问属性(不用 .value)。
  • 像操作 ref 一样可以通过 .value 整体赋值。

代码逐行详解

import { reactive, ref } from 'vue'

引入 Vue 3 的响应式核心 API:reactive(对象响应式)和 ref(引用响应式)。


export function reactiveRef(initialValue) {

定义一个名为 reactiveRef 的函数,接收初始值参数 initialValue


  const _ref = ref(initialValue)

ref 包裹初始值,生成一个响应式引用 _ref_ref.value 就是当前的真实数据。


  const proxy = reactive({
    get value() {
      return _ref.value
    },
    set value(val) {
      _ref.value = val
      // 如果你希望自动把新对象变成响应式,可以加下面这句
      // Object.assign(proxy, val)
    }
  })
  • 定义一个响应式对象 proxy,它有一个 value 属性。
  • get value() 返回 _ref.value,即当前值。
  • set value(val) 可以整体替换 _ref.value,实现 ref 的“整体赋值”效果。
  • 注释提到如果你想让新对象的属性也自动变成响应式,可以用 Object.assign(proxy, val),但这通常不是必需的。

  // 代理内部属性访问,实现不用.value即可访问
  return new Proxy(proxy, {
    get(target, key, receiver) {
      if (key === 'value') return target.value
      // 允许直接访问和设置属性
      return Reflect.get(target.value, key, receiver)
    },
    set(target, key, value, receiver) {
      if (key === 'value') {
        target.value = value
        return true
      }
      // 允许直接设置属性
      target.value[key] = value
      return true
    },
    ownKeys(target) {
      return Reflect.ownKeys(target.value)
    },
    getOwnPropertyDescriptor(target, key) {
      return Object.getOwnPropertyDescriptor(target.value, key)
    }
  })
}

这里是重点:

  • 使用 Proxy 代理 proxy 对象。
  • get:
    • 如果访问的是 value 属性,返回 proxy.value,就是 _ref.value
    • 如果访问的是其它属性(比如 state.name),实际上是取 _ref.value.name
  • set:
    • 如果设置的是 value 属性,就是整体赋值(state.value = { ... })。
    • 否则,就是给 _ref.value 的属性赋值(state.name = 'xxx')。
  • ownKeysgetOwnPropertyDescriptor
    • 让你可以像普通对象一样遍历和获取属性描述符,比如 Object.keys(state)for...in 等都能正常用。

总结

这个函数的作用:

  • 你可以用 state = reactiveRef({ ... }) 创建响应式对象。
  • state.xxx 直接访问和设置属性,不用 .value
  • state.value = { ... } 可以整体替换对象,像 ref 一样。
  • 本质上是一个“带整体赋值功能的 reactive 对象”。

使用示例

const state = reactiveRef({ name: 'Alice', age: 20 })

console.log(state.name) // 'Alice'
state.age = 22          // 响应式修改属性
state.value = { name: 'Bob', age: 30 } // 整体替换
console.log(state.name) // 'Bob'

如果你想了解 Proxy 的细节,或者响应式的原理,请随时追问!