原创
like 2
好奇一研之vue2计算属性原理实现
2020.12.25 17:46
2 人喜欢
793 次阅读
0 条评论
原本是打算研究下vue3的watchEffect是怎么实现的,捕捉里面的响应式值,某个响应式值发生改变时,则触发console.log输出。。
最简单的思路就是在watchEffecct监听里把console.log这个函数重写,通过arguments判断传进来的值是否为响应式,因为vue3在每个响应式变量上都打上了__v_isReactive的标记,判断是响应式的话,则将此arguments加进这个响应式变量的依赖数组里,而发生改变时,则将这个依赖数组遍历一遍console.log输出就好。。
而此时想着想着,突然想到vue2的计算属性?觉得特神奇。。
为什么函数里嵌着响应式属性,而这个函数就能也跟着响应式呢
核心核心核心核心还是Object.defineProperty
在指定属性get的时候触发defineProperty
在指定属性set的时候也触发defineProperty
它就是这些功能的桥梁
而我们的计算属性声明时的函数,在执行时,如若函数里有响应式数据的操作, 则会触发get/set,而这时候就可以拦截到,进行进一步的操作啦,计算属性莫过于此,油然而生
直接上码
function normalizeInstance(instance) {
let sourceData = {},
proxyData = {};
const computedDepends = {};
let computedCall
function normalizeData(data) {
sourceData = JSON.parse(JSON.stringify(data));
for (let key in data) {
Object.defineProperty(proxyData, key, {
get() {
// (computedDepends依赖数组点)
if (computedCall) {
(computedDepends[key] ??= []).push(computedCall);
}
return sourceData[key];
},
set(newVal) {
sourceData[key] = newVal;
// (computedDepends依赖数组点)
// 当重新赋值,发生改变时,则更新一遍所有依赖
computedDepends[key]?.forEach((fun) => fun());
},
});
}
}
function normalizeComputed(computed) {
for (let key in computed) {
const fun = computed[key];
if (typeof fun === "function") {
let val;
/*
初始化下val,会触发fun()方法,而fun()方法可能会触发data数据的get
例(计算count点)中的this.num1就触发了data数据的get,key则为num1
而触发时,则可以将更新通知(computedCall赋值点)加入num1的依赖数组(computedDepends依赖数组点)里
若不触发,那肯定嘛事不干啦
*/
/* (computedCall赋值点) */
(computedCall = function () {
// 改变fun内部的this指向,让其使用this指向操作时可以拦截到
val = fun.call(proxyData);
})();
Object.defineProperty(proxyData, key, {
get() {
return val;
},
});
} else {
throw new Error("computed不允许传入普通值哦亲,只允函数哦亲");
}
}
computedCall = null
}
// 先处理data的先,后面处理计算属性时,会用到核心的data
normalizeData(instance.data());
normalizeComputed(instance.computed);
return proxyData;
}
var instance = {
data() {
return {
num1: 1,
num2: 19,
};
},
computed: {
/* 计算count点 */
count() {
console.log("是的,发生改变了");
return this.num1 * this.num2;
},
test() {
return "我就单纯返回一个东西,不拿data的数据作计算";
},
},
};
var data = normalizeInstance(instance);
data.count // 19
data.num1 = 3
data.count // 57
data.num2 = 10
data.count // 30
data.test // "我就单纯返回一个东西,不拿data的数据作计算"
// 默不作声的test里并没有掺杂响应式数据,所以也没有触发data的拦截,自然是一个初始化后就诈尸的死函数了…哈哈

接下来就到watchEffect了…它是如何做到数据发生改变时,能按需console输出的,下篇见...