vue 2.x响应式系统底层依赖object.defineproperty,通过含get/set的存取描述符实现依赖收集与更新通知;需设configurable:true和enumerable:true以支持重定义与遍历,value/writable不用因采用闭包捕获值并递归劫持嵌套对象。

Vue.js 2.x 的响应式系统底层依赖 Object.defineProperty,而属性描述符(descriptor)正是控制响应行为的核心配置对象。它不是简单地存取值,而是通过精确设定属性的访问与修改逻辑,让数据变化可被侦测、可被追踪。
descriptor 中最关键的两个字段:get 和 set
Vue 在初始化 data 时,会对每个属性调用 Object.defineProperty,并传入含 get 和 set 的 descriptor:
-
get:在读取属性(如
vm.msg)时触发,用于收集当前 watcher(依赖),即“谁在用这个值”; -
set:在赋值(如
vm.msg = 'hello')时触发,通知所有依赖更新,即“谁该重新渲染”。
注意:get 不直接返回原始值,而是先进行依赖收集(Dep.target),再返回值;set 会比对新旧值(避免重复触发),触发 notify 通知更新。
为什么必须设 configurable: true 和 enumerable: true?
Vue 需要保证响应式属性能被正常遍历和后续重定义:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
-
configurable: true:允许后续再次调用
Object.defineProperty修改该属性(例如组件重新渲染时可能需更新 descriptor); -
enumerable: true:确保
for...in、Object.keys()能访问到该属性,便于 Vue 初始化时遍历 data 所有字段。
默认值是 false,若不显式设置,会导致属性不可枚举、不可重配,破坏响应式初始化流程。
value 和 writable 为什么在 Vue 响应式中基本不用?
Vue 选择使用 存取描述符(accessor descriptor),而非数据描述符(data descriptor):
- 一旦设置了
get或set,就不能再同时设置value或writable——两者互斥; - Vue 不把原始值存为
descriptor.value,而是闭包捕获变量val,由get返回、set更新,这样能支持嵌套对象递归劫持; - 因此,
writable对响应式无意义,真正控制“是否可写”的是set函数内部逻辑(比如只允许非全等变更才触发更新)。
descriptor 如何支持深层响应式?
Vue 的 defineReactive 是递归的:
- 当
val是对象时,在set中不会直接替换整个对象,而是先调用observe(val)对其再做一次响应式处理; - 这意味着每个嵌套层级的属性都会有自己的 descriptor,各自拥有独立的
get/set; - 但数组等特殊类型需额外处理(Vue 2 重写了数组原型方法),因为
Object.defineProperty无法监听索引赋值或 length 变更。
这种逐层定义 descriptor 的方式,构成了 Vue 2 响应式系统的树状监听结构。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










