跳转到内容

模板自动解包 Ref

刀刀
0字
0分钟
2026/8/16

Vue中,用 ref 声明的响应式变量,在 script 中使用时,需要通过 .value 来访问其值;而在模板 template 中,却可以直接访问其值。这是因为在模板中,Vue会自动解包 ref,使其可以直接访问其值。

模板自动解包 Ref 是通过底层调用 proxyRefs 函数实现的。

ts
export enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw',
  IS_REF = '__v_isRef',
}
ts
import { ReactiveFlags } from './constants'

const shallowUnwrapHandlers: ProxyHandler<any> = {
  get: (target, key, receiver) =>
    key === ReactiveFlags.RAW
      ? target
      : unref(Reflect.get(target, key, receiver)),
  set: (target, key, value, receiver) => {
    const oldValue = target[key]
    if (isRef(oldValue) && !isRef(value)) {
      oldValue.value = value
      return true
    } else {
      return Reflect.set(target, key, value, receiver)
    }
  },
}

export function proxyRefs<T extends object>(
  objectWithRefs: T,
): ShallowUnwrapRef<T> {
  return isReactive(objectWithRefs)
    ? (objectWithRefs as ShallowUnwrapRef<T>)
    : new Proxy(objectWithRefs, shallowUnwrapHandlers)
}

proxyRefs 函数接收一个对象,并返回一个新的 Proxy 对象,它的 getset 方法都被重写了。

get 方法中,如果访问的属性是 ReactiveFlags.RAW,则直接返回原对象;否则,调用 unref 函数,将 ref 自动解包,并返回其值。

亮点

源码着重做了几处判断:

  1. isReactive(objectWithRefs):判断传入的对象是否是由 reactive 声明的响应式对象。

    如果是,则直接返回原对象,不进行代理。因为 reactive 声明的响应式对象,其属性已经是响应式的,且不需要做 .value 的解包处理。

    反之,通过 new Proxy 创建一个代理对象,并重写其 getset 方法。让 template 中访问的属性也能保持响应式。

  2. get 获取值时,key === ReactiveFlags.RAW:判断访问的属性是否是 ReactiveFlags.RAW

    如果是,说明用户已经用 toRaw 函数将响应式对象转为了普通对象,此时不需要做 .value 的解包处理。

    反之,用 unref 函数将 ref 自动解包,并返回其值。

  3. set 设置值时,isRef(oldValue) && !isRef(value):判断旧值是否是 ref,且新值不是 ref

    如果是,说明用户试图将一个非 ref 的值赋给一个 ref,此时需要将新值赋给 ref.value,而不是直接赋给 ref

    反之,直接将新值赋给 ref

使用

proxyRefs 函数的使用示例:

component.ts
ts
const HelloWorld = {
  setup() {
    const msg = ref('Hello World')
    console.log(msg.value)
    return {
      msg,
    }
  },
}

const setupState = HelloWorld.setup()
console.log(setupState.msg) // RefImpl { value: 'Hello World' }

const proxy = proxyRefs(setupState)
console.log(proxy.msg) // 'Hello World'

总结

模板自动解包 Ref 通过 proxyRefs 函数实现。

该函数创建代理对象,拦截属性访问和设置。在模板中访问 ref 时,get 方法自动调用 unref 解包返回值,无需手动添加 .value。只有当属性是 ReactiveFlags.RAW 时才返回原始对象。

设置值时,若旧值为 ref 而新值非 ref,则更新 ref.value。此机制避免了重复代理,保持了响应式特性,使模板中使用 ref 更加便捷自然。

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀

页面历史

刀刀博客累计访客 人;文档累计访问量共