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 }