type
status
date
slug
summary
tags
category
icon
password
JavaScript 的
Proxy 是一种强大的元编程能力,允许我们拦截并自定义对象的基本操作(如属性查找、赋值、枚举、函数调用等)。然而,一个关键的限制是:Proxy 只能作用于对象(包括函数和数组),不能直接用于基本数据类型(如 String, Number, Boolean, Null, Undefined, Symbol, BigInt)。为什么不行?
根本原因在于
Proxy 的设计机制。它通过创建一个代理对象来包装目标对象,并在代理对象上设置“陷阱”(handler methods)来拦截对目标对象的操作。基本数据类型在 JavaScript 中是不可变 (immutable) 的,并且它们本身不是对象,不具备可以被拦截的内部属性或方法槽位(slots)。尝试用
Proxy 直接包装一个基本数据类型会导致 TypeError,因为 Proxy 的构造函数要求其第一个参数(target)必须是一个对象。追踪变化的变通方法:包装在对象中
虽然我们无法直接追踪一个局部变量(如上例中的
primitiveValue)的读写,但原生 JavaScript 允许我们追踪对象属性的读写。因此,要实现对基本类型值变化的“监听”,标准的做法是将该基本类型值包装在一个对象中,然后对这个包装对象使用
Proxy 或其他机制(如 getter/setter)。方法一:使用普通对象属性
这是最常见且与 Vue
ref 原理最相似的方法。方法二:自定义包装器与回调
我们也可以创建一个自定义的类或构造函数来包装值,并通过特定的方法(如
setValue)来触发回调。这更像是一种事件模式,而不是直接的拦截。与 Vue 3
ref 的联系Vue 3 的
ref() 函数正是巧妙地运用了将基本类型包装在对象中的策略来实现响应式。当你创建一个 ref 时:ref(0) 实际上创建了一个特殊的对象(一个 RefImpl 实例)。这个对象内部持有一个 _value 属性来存储实际的基本类型值。你必须通过 .value 属性来访问或修改这个值:这个
.value 属性至关重要。Vue 在 RefImpl 对象上使用了 getter 和 setter:- Getter (
.value的读取操作): 当你在组件渲染期间读取count.value时,Vue 的响应式系统会触发 getter,记录下当前组件“依赖”了这个ref。
- Setter (
.value的写入操作): 当你给count.value赋新值时,会触发 setter。Vue 在 setter 中检测到值的变化,并通知所有依赖这个ref的组件进行重新渲染。
总结
Proxy不能直接拦截对基本数据类型的操作,因为它需要一个对象作为目标。
- 要追踪基本类型值的变化,需要将其包装在一个对象里,然后监听该对象属性的访问和修改。
- Vue 3 的
ref()就是这种包装策略的典型实现,它创建一个包含.value属性的对象,并通过拦截对.value的 get 和 set 操作来实现对基本类型值的响应式追踪。.value属性是连接基本类型值和 Vue 响应式系统的桥梁。
- 作者:90_blog
- 链接:https://blog.tri7e.com/article/vue_proxy_ref
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
