Vue2 面试题
1-Vue2.0 响应式数据的原理
整体思路是数据劫持+观察者模式。对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现
当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。相关代码如下
javascript
class Observer {
// 观测值
constructor(value) {
this.walk(value);
}
walk(data) {
// 对象上的所有属性依次进行观测
let keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let value = data[key];
defineReactive(data, key, value);
}
}
}
// Object.defineProperty 数据劫持核心 兼容性在 ie9 以及以上
function defineReactive(data, key, value) {
observe(value); // 递归关键
// 如果 value 还是一个对象会继续走一遍 odefineReactive 层层遍历一直到 value 不是对象才停止
Object.defineProperty(data, key, {
get() {
console.log("获取值");
//需要做依赖收集过程 这里代码没写出来
return value;
},
set(newValue) {
if (newValue === value) return;
console.log("设置值");
//需要做派发更新过程 这里代码没写出来
value = newValue;
},
});
}
export function observe(value) {
// 如果传过来的是对象或者数组 进行属性劫持
if (Object.prototype.toString.call(value) === "[object Object]" ||Array.isArray(value)) {
return new Observer(value);
}
}2-Vue 如何检测数组变化
数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,而是选择对 7 种数 push、shift、pop、splice、unshift、sort、reverse 方法进行重写(AOP 切片思想)
所以在 Vue 中修改数组的索引和长度是无法监控到的。需要通过以上 7 种变异方法修改数组才会触发数组对应的 watcher 进行更新
javascript
// src/obserber/array.js
// 先保留数组原型
const arrayProto = Array.prototype;
// 然后将 arrayMethods 继承自数组原型
// 这里是面向切片编程思想(AOP)--不破坏封装的前提下,动态的扩展功能
export const arrayMethods = Object.create(arrayProto);
let methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"sort",
];
methodsToPatch.forEach((method) => {
arrayMethods[method] = function (...args) {
// 这里保留原型方法的执行结果
const result = arrayProto[method].apply(this, args);
// this 代表的就是数据本身 比如数据是 {a:[1,2,3]} 那么使用 a.push(4) this 就是 a
// ob 就是 a.__ob__ 这个属性就是上段代码增加的 代表的是该数据已经被响应式观察过了指向Observer实例
const ob = this.__ob__;
// 这里的标志就是代表数组有新增操作
let inserted;
switch (method) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
default:
break;
}
// 如果有新增的元素 inserted 是一个数组 调用 Observer 实例的 observeArray 对数组每一项进行观测
if (inserted) {
ob.observeArray(inserted)
};
// 之后还可以在这里检测到数组改变了之后从而触发视图更新的操作--后续源码会揭晓
return result;
};
});