vue中Vue.set()的使用以及对其进行深入解析
天命爱心职责~ 人气:0Vue.set()使用
vue 在实例上添加新的属性的时候,该属性,并不是响应式的。同样删除某一属性的时候,也不会实时渲染到页面上。
比如:
<p> 年龄:{{obj.age? obj.age: "无"}}</p>
···········
data() {
return {
obj:{ name:"Lena", id:1 },
}
}
页面上 显示的是 年龄:无 现在需要添加一个响应式的属性 age 。
<template> <div class="app"> <ul> <li> 年龄:{{obj.age? obj.age: "无"}}</li> </ul> <button @click="add()">添加年龄属性</button> <button @click="show()">打印</button> </div> </template> <script> import Vue from 'vue' export default { component:{}, data() { return { obj:{ name:"Lena", id:1 }, } }, methods: { add(){ this.obj.age= 20 }, show(){ console.log('obj',this.obj) } } } </script>
效果:
通过 this.obj.age= 20 ,控制台打印已经有了该属性,并没有渲染到页面上。 可见,这种方式添加的属性 age 并不是响应式的。
使用Vue.set() ,更改add()方法:
add(){ Vue.set(this.obj,'age', '20') },
效果:
因为vue不能检测到对象属性的添加或者删除,只有在data对象上存在的属性是响应式的,所以要使用Vue.set()方法将响应式属性添加到对象上。同样的道理,删除对象 Vue.delete也是如此。
Vue.delete()的使用
<template> <div class="app"> <ul> <li> 年龄:{{obj.age? obj.age: "无"}}</li> </ul> <button @click="add()">添加年龄属性</button> <button @click="del()">删除年龄属性</button> <button @click="show()">打印</button> </div> </template> <script> import Vue from 'vue' export default { component:{ }, data() { return { obj:{ name:"Lena", id:1 }, } }, methods: { add(){ Vue.set(this.obj,'age', '20') }, del(){ Vue.delete(this.obj,'age') }, show(){ console.log('obj',this.obj) } } } </script>
效果:
del() 方法如果是下面两种,同样不是响应式的。
del(){
delete this.obj.age
},
或者:
del(){
this.obj = { name:"Lena", id:1 }
},
Vue.set()方法原理解析
我们找到封装set方法的地方:.........\node_modules\vue\src\core\observer\index.js
找到封装的set方法:
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 }
对象调用: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] = val return 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的原理。
加载全部内容