$set
用法
set 是全局 Vue.set 的别名。
set(target, propertyName/index, value)
作用
Vue 中对于对象新增的属性或者是数组新增的下标都是不能够监听到的,为了保障新的属性同样是响应式的,且触发视图更新,需要借助 this.$set 来新增属性。
对象不能是 Vue 实例或者是 Vue 实例的根数据对象。
原理
// src/core/observer/index.js
function set (target: Array<any> | Object, key: any, val: any): any {
// 判断非生产环境下传入的 target 是否为 undefined、null 或原始类型
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
// 如果是,就抛出警告
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 如果传入的是数组 并且 key 是有效的数组索引。
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 取数组长度和 key 这两者最大的值作为数组的新长度
target.length = Math.max(target.length, key)
// 数组的splice方法将传入的索引key对应的val值添加进数组
target.splice(key, 1, val)
// 把值返回
return val
}
// 如果是对象
//首先判断 key 是否存在于 target 中。
if (key in target && !(key in Object.prototype)) {
// 修改操作, 只修改属性值即可。
target[key] = val
return val
}
// __ob__ 标志着是否已经是响应式的了。
const ob = (target: any).__ob__
// target 是否是 Vue 实例,或者 vue 实例的根数据对象
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 添加属性
target[key] = val
return val
}
// 是对象,并且是响应式的,将新属性添加到 target 上。
defineReactive(ob.value, key, val)
// 通知依赖跟新
ob.dep.notify()
// 返回值
return val
}
可以看出来,就是通过不断的判断,先判断 target 的类型是否为 null、undefined、原始数据类型。然后判断是否为数组,然后判断是否为对象.
$delete
vm.$delete 是全局 Vue.delete 别名。
vm.$delete(target, propertyName, index)
删除对象的属性,能够删除后触发视图的更新。属性被删除是不能够被检测到的。
源码
function del (target: Array<any> | Object, key: any) {
// 判断非生产环境下传入的 target 是否为 undefined、null 或原始类型
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
// 警告
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 如果 target 是数组, 并且 key 是有效索引
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 使用 splice 来删除掉 key 的数组元素。
target.splice(key, 1)
return
}
// __ob__ 标志着是否已经是响应式的了。
const ob = (target: any).__ob__
// target 是否是 Vue 实例,或者 vue 实例的根数据对象
if (target._isVue || (ob && ob.vmCount)) {
// 警告
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// key 是否存在于 target 上, 不存在就不用删除
if (!hasOwn(target, key)) {
return
}
// 从属性中删除
delete target[key]
// 当前的 target 是否是响应式对象
if (!ob) {
// 如果不是响应式就删除后直接返回
return
}
// 是响应式的,就通知依赖更新
ob.dep.notify()
}