话不多说上源码,其实问题就是在于修改数组例如 arr=[1,2,3,4] arr[1]=2,类似这种直接用下标修改整个值得无效, 如果式arr=[{name:“aaa”}] arr[0].name="ccc"这种是可以得 arr[0]={name:“ccc”} 这是不行得
**
- 源码
**
function set (target: Array<any> | Object, key: any, val: any): any {
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)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 对象
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
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
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
首先warn警告我们可以忽略
1.第一先判断是否为数组
如果是得话,他将目标对象得长度设为target.length, key两者大得那一个,通过splice方法去修改。
问题就在这splice方法如何响应,其实vue在初始化数据得时候会将数据通过object.defineproperty来做一个响应式处理,数组比较特殊,需要通过修改他得原型链,重写数组得方法来实现响应式,下面第二份代码
// 数组
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
重写得方法中去将对应得值响应式化,然后再调用原型链上对应得方法就可以了
// 扩展数组得方法 使其响应式变化 这里就例举4个方法
let ARRAY_METHOD = ["push", "pop", "shift", "unshift"];
// 继承关系 arr->Array.prototype->Object.prototype.....
// 修改继承关系 arr->改写得方法->Array.prototype->Object.prototype.....
// 创建一个对象
let array_method = Object.create(Array.prototype);
ARRAY_METHOD.forEach((method) => {
// 重写array_method中得对应方法 函数拦截
array_method[method] = function () {
// 将数据响应式化 arguments代表push 进来得值 比如{name:“111”}
for (let item of arguments) {
Observer(item);
}
// 调用原来得方法 Array.prototype[method] 正常操作
let res = Array.prototype[method].apply(this, arguments);
return res;
};
});
2.第二部 判断对象这个没什么好说得vue本就支持对象得响应式