You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionset(target,key,val){if("development"!=='production'&&(isUndef(target)||isPrimitive(target))){warn(("Cannot set reactive property on undefined, null, or primitive value: "+((target))));}if(Array.isArray(target)&&isValidArrayIndex(key)){target.length=Math.max(target.length,key);target.splice(key,1,val);returnval}if(keyintarget&&!(keyinObject.prototype)){target[key]=val;returnval}varob=(target).__ob__;if(target._isVue||(ob&&ob.vmCount)){"development"!=='production'&&warn('Avoid adding reactive properties to a Vue instance or its root $data '+'at runtime - declare it upfront in the data option.');returnval}if(!ob){target[key]=val;returnval}defineReactive(ob.value,key,val);ob.dep.notify();returnval}
Vue数据更新异常处理机制
Vue 数据根据异常处理机制主要是聊下关于 Vue 对于 Object 类型的响应式数据添加属性,以及对于 Array 类型数据变更的监测。由于Vue 是使用 Object.defineProperty 来做数据劫持,当被劫持的 key 所对应的 value 为基本类型时,那么每次对这个 key 的 value 做修改的时候,都会调用这个 key 所定义的 setter 函数,这样也就能触发相关的订阅者的更新。但是如果 key 所对应的 value 为引用类型,例如 plain object,这个时候如果你直接改变了引用类型的地址,那么会触发 key 对应的 setter 函数,使得相关的订阅者进行更新。但是如果你对 plain object 进行 增加/删除 属性的操作的话,这个时候是无法触发 key 所对应的 setter 函数的,那么也就无法更新相关的 watcher。如果遇到这种场景的话,那应该怎么处理呢?
Vue 提供了全局 Vue.set/Vue.del 和实例上的 vm.$set/vm.$del 方法去完成 plain object 属性的添加和删除:
页面渲染出来后,
obj.b
的文本内容是无法显示出来的,具体的原因我上面也说了,这时如果在定时器内部调用:那么1s后,页面可以渲染出
obj.b
的内容。这是因为在 set 方法内部,首先会获取(target).__ob__
属性,即这个 key 所对应的 plain object 的 observer 观察者,通过defineReactive(ob.value, key, val)
方法,将新传入的值变更为响应式的数据后调用 ob.dep.notify() 方法去遍历所有收集起来的观察者(watcher),并触发观察者的更新,如果这个 watcher 是 render watcher,那么就能完成视图的更新。那么在这个组件的生命周期中,这个 key 所对应的 observer 是在什么时候收集到 render watcher 的呢?事实上在组件初始化的 initData 阶段,将 data 变成响应式数据的过程中,对应到本实例就是将 obj 的值(
{ a: 'text' }
)变成响应式的过程中,首先获取 obj 的值(为 plain object)的 observer 观察者实例,然后形成一个闭包。当组件实例化进入 $mount 阶段将 VNode 转化为真实的 dom 时,会调用 obj 的 getter 函数,这个时候首先完成 obj 的依赖的添加,将 render watcher 添加到 obj 的 dep 数组当中,同时因为 obj 的值的 observer 存在,那么同时将这个 render watcher 添加到 observer 的 dep 依赖当中。这样就完成了依赖的收集,那么在实际使用过程中调用实例的 this.$set 方法去给 obj 添加属性,在这个方法内部会获取 obj 的值({ a: 'text' }
)的__ob__
属性,即{ a: 'text' }
的 observer,同时在 set 方法内部最终会调用ob.dep.notify()
方法去通知所有 watcher 去完成更新。如果你要删除一个响应式数据上的 key,那么需要调用Vue.del/vm.$del
方法去完成,相关的内容大家可以阅读对应的源码。以上是 plain object 在数据更新时需要注意的情况。另外还需要注意的就是当你的响应式数据类型为数组的时候,如何正确的去处理数组项的变更,同时去完成页面的渲染。我们首先来看个例子:
The text was updated successfully, but these errors were encountered: