面试题答案
一键面试实现思路
- 递归遍历:对于深层嵌套的对象和数组,Vue会通过递归的方式遍历数据结构。在遍历对象时,对每个属性使用
Proxy
的get
和set
陷阱;对于数组,除了对元素的访问和修改进行处理,还会对数组的一些特殊方法(如push
、pop
等)进行拦截。 - 依赖收集:在
Proxy
的get
陷阱中,当访问某个属性时,会将当前的渲染函数(或计算属性的求值函数)作为依赖收集起来。Vue使用Dep
类来管理依赖,每个属性都对应一个Dep
实例,当属性值发生变化时,Dep
会通知所有收集到的依赖进行更新。 - 懒处理:对于深层嵌套的数据,并非一次性将所有层级的依赖都建立好。而是采用“按需收集”的策略,只有当访问到某一层级的数据时,才会建立该层级数据的依赖关系,这样可以避免不必要的性能开销。
关键逻辑
Proxy
的get
方法:
const handler = {
get(target, property) {
const value = Reflect.get(target, property);
// 收集依赖
if (Dep.target) {
const dep = target[property] && target[property].__ob__ && target[property].__ob__.dep;
dep && dep.depend();
}
// 对于对象或数组类型的值,继续创建Proxy
if (typeof value === 'object' && value!== null) {
return reactive(value);
}
return value;
}
};
Proxy
的set
方法:
handler.set = function (target, property, value) {
const oldValue = target[property];
const result = Reflect.set(target, property, value);
// 如果值发生变化,通知依赖更新
if (result && oldValue!== value) {
const dep = target[property] && target[property].__ob__ && target[property].__ob__.dep;
dep && dep.notify();
}
return result;
};
- 数组方法拦截:
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const methodsToPatch = ['push', 'pop','shift', 'unshift','splice','sort', 'reverse'];
methodsToPatch.forEach(method => {
arrayMethods[method] = function (...args) {
const result = arrayProto[method].apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case'splice':
inserted = args.slice(2);
break;
}
if (inserted) ob.observeArray(inserted);
ob.dep.notify();
return result;
};
});
通过上述逻辑,Vue能够在使用Proxy
实现响应式系统时,针对深层嵌套的复杂数据结构,有效地建立依赖关系,并进行高效的更新检测与处理。