解析Vue.js中的computed工作原理

39次阅读

共计 3355 个字符,预计需要花费 9 分钟才能阅读完成。

我们通过实现一个简单版的和 Vue 中 computed 具有相同功能的函数来了解 computed 是如何工作的。写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。

JS 属性:
JavaScript 有一个特性是 Object.defineProperty , 它能做很多事,但我在这篇文章只专注于这个方法中的一个:
var person = {};
Object.defineProperty (person, ‘age’, {
get: function () {
console.log (“Getting the age”);
return 25;
}
});// 欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860
console.log (“The age is “, person.age);
// Prints:
//
// Getting the age
// The age is 25
(Obeject.defineProperty 是 Object 的一个方法,第一个参数是对象名称,第二个参数是要设置的属性名,第三个参数是一个对象,它可以设置这个属性是否可修改、可写等,而这篇文章主要使用的是 Obeject.defineProperty 的访问器属性,感兴趣的朋友可以自行 google 或者查看 Js 高及程序设计) 尽管 person.age 看起来像是访问了对象的一个属性,但其实在内部我们是运行了一个函数。
一个基本可响应的 Vue.js
Vue.js 内部构建了一个可以将普通的对象转化为可以被观察的值(响应属性),下面为大家展示一个简化版的如何添加响应属性的案例:
function defineReactive (obj, key, val) {
Object.defineProperty (obj, key, {
get: function () {
return val;
},
set: function (newValue) {
val = newValue;
}
})
};
// 创建一个对象
var person = {};
// 添加可响应的属性 ”age” 和 ”country”
defineReactive (person, ‘age’, 25);
defineReactive (person, ‘country’, ‘Brazil’);
// 现在你可以随意使用 person.age 了
if (person.age < 18) {
return ‘minor’;
}
else {
return ‘adult’;
}// 欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860
// 设置 person.country 的值
person.country = ‘Russia’;
有趣的是,25 和‘Brazil’ 还是一个闭包内部的变量,只有当赋给它们新值的时候 val 才会改变。person.country 并不拥有 ‘Brazil’ 这个值,而是 getter 这个函数拥有 ‘Brazil’ 这个值。
声明一个计算属性
让我们创建一个定义计算属性的函数 defineComputed。这个函数就跟大家平时使用 computed 时的一样。
defineComputed (
person, // 计算属性就声明在这个对象上
‘status’, // 计算属性的名称
function () { // 实际返回计算属性值的函数
console.log (“status getter called”)
if (person.age < 18) {
return ‘minor’;
}
else {
return ‘adult’;
}
},
function (newValue) {
// 当计算属性值更新时调用的函数
console.log (“status has changed to”, newValue)
}
});
// 我们可以像使用一般的属性一样使用计算属性
console.log (“The person’s status is: “, person.status);

让我们写一个简单的 defineComputed 函数,它支持调用计算方法,但目前不需要它支持 updateCallback
function defineComputed (obj, key, computeFunc, updateCallback) {
Object.defineProperty (obj, key, {
get: function () {
// 执行计算函数并且返回值
return computeFunc ();
},// 欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860
set: function () {
// 什么也不做,不需要设定计算属性的值
}
})
}
这个函数有两个问题:每次访问计算属性时都会执行一次计算函数 computeFunc () 它不知道什么时候更新(即当我们更新某个 data 中的属性,计算属性中也会更新这个 data 属性)
// 我希望最终函数执行后是这个效果:每当 person.age 更新值的时候,person.status 也同步更新
person.age = 17;
// console: status 的值为 minor
person.age = 22;
// console: status 的值为 adult
增加一个依赖项
让我们增加一个全局变量 Dep:
var Dep = {
target: null
};
这是一个依赖项,接着我们用一个骚操作来更新 defineComputed 函数:
function defineComputed (obj, key, computeFunc, updateCallback) {
var onDependencyUpdated = function () {
// TODO
}// 欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860
Object.defineProperty (obj, key, {
get: function () {
// 将 onDependencyUpdated 这个函数传给 Dep.target
Dep.target = onDependencyUpdated;
var value = computeFunc ();
Dep.target = null;
},
set: function () {
// 什么也不做,不需要设定计算属性的值
}
})
}
现在让我们回到之前设置的响应属性上:
function defineReactive (obj, key, val) {
// 所有的计算属性都依赖这个数组
var deps = [];

Object.defineProperty (obj, key, {
get: function () {
// 检查是否调用了计算属性,如果调用了,Department.target 将等于一个 onDependencyUpdated 函数
if (Dep.target) {
// 把 onDependencyUpdated 函数 push 到 deos 中
deps.push (target);
}

return val;
},
set: function (newValue) {
val = newValue;

// 通知所有的计算属性,告诉它们有个响应属性更新了
deps.forEach ((changeFunction) => {
changeFunction ();
});// 欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860
}// 面向 1 - 3 年前端人员
})
};
我们可以在计算属性定义的函数触发更新回调后更新 onDependencyUpdated 函数。
var onDependencyUpdated = function () {
// 再次计算 计算属性的值
var value = computeFunc ();
updateCallback (value);
}// 欢迎加入前端全栈开发交流圈一起学习交流:864305860
把它们整合到一起:让我们重新访问我们的计算属性 person.status :
person.age = 22;
defineComputed (
person,
‘status’,
function () {
if (person.age > 18) {
return ‘adult’;
}
},
function (newValue) {
console.log (“status has changed to”, newValue)
}// 欢迎加入前端全栈开发交流圈一起学习交流:864305860
});
console.log (“Status is “, person.status);
结语
感谢您的观看,如有不足之处,欢迎批评指正。

正文完
 0