【Vue源码】Vue不能检测到对象属性的添加或删除
vue实现数据双向绑定有这么一个过程:当你把一个普通的 JavaScript 对象传给 Vue 实例的 data
选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty() 把这些属性全部转为getter/setter。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter
被调用时,会通知 watcher
重新计算,从而致使它关联的组件得以更新。实现数据data变化更新视图view。
若一个对象的属性没有在data中声明,则他就不是响应式的。由于 Vue 会在初始化实例时对属性执行 getter/setter
转化过程,这样的话这个对象属性就是响应式的。而执行这个过程必须在data中声明才会有。
var vm = new Vue({ data:{ a:1// vm.a 是响应的 } }) vm.b = 2;// vm.b 是非响应的
要解决这个问题需要用Vue.set(object, key, value)或者vm.$set方法(全局 Vue.set 方法的别名)
Vue.set(vm.someObject, 'b', 2); //或者 this.$set(this.someObject,'b',2);
Vue.set源码(src\core\observer\index.js),会调用Object.defineProperty():
export function set (target: Array<any> | Object, key: any, val: any): any { if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target)) ) { warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`) } if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val } if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ) return val } if (!ob) { target[key] = val return val } defineReactive(ob.value, key, val) ob.dep.notify() return val }