背景
Vue响应式原理,有两种情况下修改数据Vue是不会触发视图更新的
- 实例创建之后添加新的属性到实例上
- 直接更改数组下标修改数组的值
使用场景
- 为data对象添加一个新属性
- 改变数组长度时
vue.set()与this.$set()的区别
- vue.set()是将set函数绑定在Vue构造函数上
- this.$set()是将set函数绑定在Vue原型上
解析源码
- 将传入的对象的属性变成响应式的
export function set (target: Array<any> | Object, key: any, val: any): any {
// 如果 set 函数的第一个参数是 undefined 或 null 或者是原始类型值,那么在非生产环境下会打印警告信息
// 这个api本来就是给对象与数组使用的
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)) {
// 类似$vm.set(vm.$data.arr, 0, 3)
// 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
target.length = Math.max(target.length, key)
// 利用数组的splice变异方法触发响应式, 这个前面讲过
target.splice(key, 1, val)
return val
}
// target为对象, key在target或者target.prototype上。
// 同时必须不能在 Object.prototype 上
// 直接修改即可, 有兴趣可以看issue: https://github.com/vuejs/vue/issues/6845
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 以上都不成立, 即开始给target创建一个全新的属性
// 获取Observer实例
const ob = (target: any).__ob__
// Vue 实例对象拥有 _isVue 属性, 即不允许给Vue 实例对象添加属性
// 也不允许Vue.set/$set 函数为根数据对象(vm.$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
}
// target本身就不是响应式数据, 直接赋值
if (!ob) {
target[key] = val
return val
}
// 进行响应式处理
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}