目录
Vue.set()使用
Vue.delete()的使用
Vue.set()方法原理解析
总结
vue 在实例上添加新的属性的时候,该属性,并不是响应式的。同样删除某一属性的时候,也不会实时渲染到页面上。
比如:
年龄:{{obj.age? obj.age: "无"}}
···········
data() {
return {
obj:{ name:"Lena", id:1 },
}
}
页面上 显示的是 年龄:无 现在需要添加一个响应式的属性 age 。
- 年龄:{{obj.age? obj.age: "无"}}
效果:
通过 this.obj.age= 20 ,控制台打印已经有了该属性,并没有渲染到页面上。 可见,这种方式添加的属性 age 并不是响应式的。
使用Vue.set() ,更改add()方法:
add(){Vue.set(this.obj,'age', '20')},
效果:
因为vue不能检测到对象属性的添加或者删除,只有在data对象上存在的属性是响应式的,所以要使用Vue.set()方法将响应式属性添加到对象上。同样的道理,删除对象 Vue.delete也是如此。
- 年龄:{{obj.age? obj.age: "无"}}
效果:
del() 方法如果是下面两种,同样不是响应式的。
del(){
delete this.obj.age
},
或者:
del(){
this.obj = { name:"Lena", id:1 }
},
我们找到封装set方法的地方:.........\node_modules\vue\src\core\observer\index.js
找到封装的set方法:
export function set (target: Array | 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] = valreturn 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] = valreturn val}defineReactive(ob.value, key, val)ob.dep.notify()return val
}
对象调用:Vue.set( target ,'age', 20 )
数组调用:Vue.set( array , 0, 20 ) //数组对象,索引,值
首先是判断是否是开发环境并且 对象是否被定义isUndef(target)或者是否是基础类型isPrimitive(target),否则会报错:
`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
)
如果是数组的话,调用重写的splice()方法,可以更新视图。
isValidArrayIndex(key)方法用来验证是否是一个有效的数组索引, 其实就是验证是否是一个非无穷大的正整数。
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
}
如果是Vue实例,或者是根数据data的时候,就会报错。
如果本身就不是响应式的,只需要直接赋值即可。
const ob = (target: any).__ob__//如果是Vue实例,或者是根数据data的时候,就会报错。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] = valreturn val}defineReactive(ob.value, key, val)ob.dep.notify()return val
排除各种不合适的,最后给当前对象定义一个属性:defineReactive(ob.value, key, val) 相当于用了 Object.defineProperty 重新定义了一下。
最后,手动通知视图更新:ob.dep.notify()
这个 set方法,对于数组来说,调用的就是splice,对于对象来说,使用的就是defineReactive,再添加了一个手动的视图更新。这就是set的原理。