1、使用场景
首先我们来看看官网如何说明的:由于 JavaScript 的限制,有些 Vue 无法检测的更改类型。但是,有一些方法可以规避它们以维持响应性。
-
对于对象
Vue 无法检测到 property 的添加或删除。由于 Vue 在实例初始化期间执行 getter/setter 转换过程,因此必须在 data 对象中存在一个 property,以便 Vue 对其进行转换并使其具有响应式。对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property,还可以使用 vm.$set 实例方法
-
对于数组
Vue 不能检测以下数组的变动:
当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength此时可以使用Vue.set(vm.items, indexOfItem, newValue)
2、内部实现
下面我们再来看看$set内部到底是这么实现的:
在vue/dist/vue.js里找到了如下源码
function set (target, key, val) {
if (isUndef(target) || isPrimitive(target)
) {
warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target))));
}
// 1、判断isArray且不包含key 通过splice
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
target.length = Math.max(target.length, key);
// 利用数组的splice变异方法触发响应式
target.splice(key, 1, val);
return val
}
// 2、target为对象, key在target或者target.prototype上。直接修改即可,
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val
}
// 3、创建新属性
var ob = (target).__ob__;
if (target._isVue || (ob && ob.vmCount)) {
// Vue 实例对象拥有 _isVue 属性, 即不允许给Vue 实例对象添加属性
// 也不允许Vue.set/$set 函数为根数据对象(vm.$data)添加属性
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$$1(ob.value, key, val);
ob.dep.notify();
return val
}
如有疑问或不足之处,欢迎交流指正