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取两者中的最大值。