Vue源码解析之数据响应系统的使用(13)
所以需要递归观测数组元素。
Vue.set($set) 和 Vue.delete($delete) 的实现
我们知道,为对象或数组直接添加或删除元素Vue是拦截不到的。我们需要使用Vue.set、Vue.delete去解决,Vue还在实例对象上定义了$set $delete方便我们使用。其实不管是实例方法还是全局方法它们的指向都是一样的。我们来看以下它们的定义。
$set $delete定义在core/instance/state.js文件中的stateMixin方法中
export function stateMixin (Vue: Class<Component>) {
...
Vue.prototype.$set = set
Vue.prototype.$delete = del
...
}
Vue.set和Vue.delete定义在core/global-api/index.js文件中的initGlobalAPI函数中:
export function initGlobalAPI (Vue: GlobalAPI) {
...
Vue.set = set
Vue.delete = del
...
}
可以看到它们的函数值是相同的。 set和del定义在core/observer/index.js文件中。我们先来看一下set的定义
set
从上到下来看set的函数体,显示这个if判断:
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
isUndef
export function isUndef (v: any): boolean %checks {
return v === undefined || v === null
}
判断变量是否是未定义,或者值为null。
isPrimitive
export function isPrimitive (value: any): boolean %checks {
return (
typeof value === 'string' ||
typeof value === 'number' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
判断变量是否是原始类型。
所以这个if语句的作用就是,如果target是undefined或者null或者它的类型是原始类型,在非生产环境下打印警告信息。
再看下一个if语句:
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
isValidArrayIndex
export function isValidArrayIndex (val: any): boolean {
const n = parseFloat(String(val))
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
判断变量是否是有效的数组索引。
如果target是一个数组,并且key是一个有效的数组索引,就执行if语句块内的代码
我们知道splice变异方法是可以触发响应的,target.splice(key, 1, val) 就利用了替换元素的能力,将指定位置元素的值替换为新值。所以数组就是利用splice添加元素的。另外,当要设置的元素的索引大于数组长度时 splice 无效,所以target的length取两者中的最大值。

