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

简介: 我们通过实现一个简单版的和Vue中computed具有相同功能的函数来了解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);

结语

感谢您的观看,如有不足之处,欢迎批评指正。

原文地址:https://segmentfault.com/a/1190000017356237

相关文章
|
2月前
|
JavaScript 前端开发 开发者
Nest.js控制器深度解析:路由与请求处理的高级特性
以上就是对 NestJS 控制层高级特性深度解析:从基本概念到异步支持再到更复杂场景下拦截其与管道等功能性组件运用都有所涉及,希望能够帮助开发者更好地理解和运用 NestJS 进行高效开发工作。
277 15
|
2月前
|
JavaScript 前端开发 IDE
TypeScript vs. JavaScript:技术对比与核心差异解析
TypeScript 作为 JavaScript 的超集,通过静态类型系统、编译时错误检测和强大的工具链支持,显著提升代码质量与可维护性,尤其适用于中大型项目和团队协作。相较之下,JavaScript 更灵活,适合快速原型开发。本文从类型系统、错误检测、工具支持等多维度对比两者差异,并提供技术选型建议,助力开发者合理选择。
377 1
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
663 0
|
2月前
|
存储 JavaScript 前端开发
JavaScript 语法全面解析
JavaScript 语法体系丰富且不断更新,从基础的变量声明、数据类型,到复杂的函数、对象、异步语法,每个知识点都需要开发者深入理解并灵活运用。本文梳理的 JS 语法核心内容,可为开发者提供系统的学习框架,后续还需通过大量实践(如编写交互组件、实现业务逻辑)巩固知识,逐步提升 JS 编程能力,应对前端开发中的各类挑战。
|
6月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
666 77
|
5月前
|
机器学习/深度学习 JavaScript 前端开发
JS进阶教程:递归函数原理与篇例解析
通过对这些代码示例的学习,我们已经了解了递归的原理以及递归在JS中的应用方法。递归虽然有着理论升华,但弄清它的核心思想并不难。举个随手可见的例子,火影鸣人做的影分身,你看到的都是同一个鸣人,但他们的行为却能在全局产生影响,这不就是递归吗?雾里看花,透过其间你或许已经深入了递归的魅力之中。
200 19
|
5月前
|
JavaScript 前端开发 UED
Vue 手风琴实现的三种常用方式及长尾关键词解析
手风琴效果是Vue开发中常见的交互组件,可节省页面空间、提升用户体验。本文介绍三种实现方式:1) 原生Vue结合数据绑定与CSS动画;2) 使用Element UI等组件库快速构建;3) 自定义指令操作DOM实现独特效果。每种方式适用于不同场景,可根据项目需求选择。示例包括产品特性页、后台菜单及FAQ展示,灵活满足多样需求。附代码示例与资源链接,助你高效实现手风琴功能。
226 10
|
5月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
242 1
|
6月前
|
JSON 前端开发 Serverless
Mock.js 语法结构全解析
Mock.js 的语法规范介绍,从数据模板定义规范和数据占位符定义规范俩部分介绍, 让你更好的使用 Mock.js 来模拟数据并提高开发效率。

热门文章

最新文章

推荐镜像

更多
  • DNS