解析Vue.js中的computed工作原理
我们通过实现一个简单版的和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);
结语
感谢您的观看,如有不足之处,欢迎批评指正。
上一篇: 01 Oracle分区索引