Vue.set :修改数据,并触发视图更新。
BTW : $set是绑定在Vue原型上的set方法,两者并无二致。
因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi') ,即无法检测到数组,对象的值修改,从而导致页面无法显示实时的数据。
如何使用Vue.set ?
Vue.set( target, propertyName/index, value )
let arr = [1,2,3];
this.set(arr,1,'修改arr数组的第二位数据')
more Play --------------------------------------我是分割线
data:{
arr: [1,12,34,5,1],
user: [
{name: 'Sean', age: 20},
{name: 'Zan', age: 3}
],
userInfo:{
name: 'Billy'
}
}
//1\. 将arr数组中索引为1的值修改为'zan'
Vue.set(this.arr, 1, 'zan'),
//2\. 给user数组添加对象
Vue.set(this.user, 2, {name: 'Billy', age: 10}),
//3\. 修改user数组索引为0的对象属性值
Vue.set(this.user, 0, {name: 'Jim', age: 42}),
//4\. 给对象添加属性
Vue.set(this.userInfo, 'age', 29)
啥时候需要用到set方法呢?
修改数据后,并没有触发视图更新。就应该考虑使用这个。
Vue在初始实例时进行数据绑定,使用了Object.defineProperty()绑定getter和setter,即此时的data中数据进行了双向绑定。那么如果要添加新的属性,便不是响应式。会导致数据变化,页面不变的情况。
使用了set方法后即做了双向绑定。解决上述问题。
尝试解析 Vue.set的原理:
(部分代码,来源于网络)
注释纯手写,各位看官不妨看一看~。
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)}`)
}
// 数组
// Vue封装过的数组7个方法,push splice等等才会触发页面渲染,这里使用了splice直接触发页面渲染。
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 对象
// 假设key本身就是对象的某个属性,修改既可。例如本身就是data中的属性,修改,会直接触发页面渲染。
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 在vue中,如果某个对象存在__ob__属性,那么说明这个对象是响应式的。
// 如此我们在这里进行__ob__属性的判断是否存在,如果存在,那么说明当前对象是响应式的,修改数据,页面会直接进行渲染。
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
}