Vue中的Computed实现原理分析

 更新时间:2024年08月07日 09:55:26   作者:秦JaccLink  
这篇文章主要介绍了Vue中的Computed实现原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

在 Vue.js 中,computed 属性是一种强大的特性,用于定义依赖于其他响应式数据的计算值。

computed 属性不仅能够简化模板中的表达式,还能够缓存计算结果,避免不必要的重复计算,从而提高性能。

将深入探讨 Vue 中 computed 属性的实现原理,包括其工作机制、依赖追踪、缓存策略等方面。

1. Computed 属性概述

Computed 属性是 Vue 实例中的一个特殊属性,它允许开发者定义一个计算值,该值依赖于其他响应式数据。

Computed 属性具有以下特点:

  • 响应式:当依赖的数据发生变化时,computed 属性会自动重新计算。
  • 缓存:computed 属性会缓存计算结果,只有当依赖的数据发生变化时,才会重新计算。
  • 惰性求值:computed 属性在首次访问时才会进行计算,之后会根据依赖数据的变化情况决定是否重新计算。

2. Computed 属性的基本用法

在 Vue 实例中,可以通过 computed 选项来定义 computed 属性。

new Vue({
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    };
  },
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  }
});

在上述代码中,fullName 是一个 computed 属性,它依赖于 firstNamelastName

firstNamelastName 发生变化时,fullName 会自动重新计算。

3. Computed 属性的实现原理

3.1 依赖追踪

Vue 的 computed 属性实现依赖于 Vue 的响应式系统。

Vue 通过 Object.definePropertyProxy 来劫持数据的变化,并在数据变化时通知依赖该数据的观察者。

3.1.1 响应式数据劫持

Vue 在初始化数据时,会通过 Object.definePropertyProxy 对数据进行劫持,使其变为响应式数据。

function defineReactive(obj, key, val) {
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      if (Dep.target) {
        dep.depend();
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      dep.notify();
    }
  });
}

在上述代码中,defineReactive 函数通过 Object.defineProperty 劫持了对象的属性,并在 getset 方法中分别收集和通知依赖。

3.1.2 依赖收集

在 computed 属性被访问时,Vue 会通过 Dep.target 来收集依赖。

functionWatcher(vm, expOrFn, cb) {
  this.vm = vm;
  this.getter = parsePath(expOrFn);
  this.cb = cb;
  this.value = this.get();
}

Watcher.prototype.get = function() {
  Dep.target = this;
  const value = this.getter.call(this.vm, this.vm);
  Dep.target = null;
  return value;
};

Watcher.prototype.update = function() {
  const oldValue = this.value;
  this.value = this.get();
  this.cb.call(this.vm, this.value, oldValue);
};

在上述代码中,Watcher 实例在 get 方法中将自身设置为 Dep.target,然后访问 computed 属性,从而触发依赖数据的 get 方法,完成依赖收集。

3.2 缓存策略

Computed 属性具有缓存机制,只有在依赖数据发生变化时,才会重新计算。

3.2.1 缓存实现

Vue 通过 Watcher 实例的 dirty 属性来控制缓存。

function Watcher(vm, expOrFn, cb, options) {
  this.vm = vm;
  this.getter = expOrFn;
  this.cb = cb;
  this.dirty = this.lazy = !!options.lazy;
  this.value = this.lazy ? undefined : this.get();
}

Watcher.prototype.evaluate = function() {
  this.value = this.get();
  this.dirty = false;
};

Watcher.prototype.get = function() {
  pushTarget(this);
  let value;
  const vm = this.vm;
  try {
    value = this.getter.call(vm, vm);
  } finally {
    popTarget();
  }
  return value;
};

Watcher.prototype.update = function() {
  if (this.lazy) {
    this.dirty = true;
  } else {
    this.run();
  }
};

在上述代码中,Watcher 实例的 dirty 属性用于标记 computed 属性是否需要重新计算。

当依赖数据发生变化时,Watcherupdate 方法会将 dirty 设置为 true,表示需要重新计算。

3.2.2 惰性求值

Computed 属性在首次访问时才会进行计算,之后会根据 dirty 属性决定是否重新计算。

function createComputedGetter(key) {
  return function computedGetter() {
    const watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value;
    }
  };
}

在上述代码中,createComputedGetter 函数返回一个 computed 属性的 getter 函数。

在访问 computed 属性时,如果 dirtytrue,则会调用 watcher.evaluate 方法进行计算,并将 dirty 设置为 false,表示计算结果已缓存。

4. Computed 属性的优化

4.1 避免不必要的计算

在定义 computed 属性时,应尽量避免不必要的计算。

例如,如果 computed 属性的计算逻辑较为复杂,可以考虑将其拆分为多个简单的 computed 属性。

computed: {
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  formattedName() {
    return this.fullName.toUpperCase();
  }
}

4.2 使用 Watcher 进行性能优化

在某些情况下,可以使用 watch 选项来替代 computed 属性,以实现更细粒度的控制和性能优化。

watch: {
  firstName: 'updateFullName',
  lastName: 'updateFullName'
},
methods: {
  updateFullName() {
    this.fullName = `${this.firstName} ${this.lastName}`;
  }
}

5. 总结

Vue 的 computed 属性通过依赖追踪和缓存策略,实现了响应式计算和性能优化。

在实现原理上,computed 属性依赖于 Vue 的响应式系统,通过 Watcher 实例进行依赖收集和缓存控制。

通过深入理解和掌握 computed 属性的实现原理,开发者可以更好地利用这一特性,提高应用的性能和可维护性。

在实际开发中,应根据具体需求合理使用 computed 属性,并结合其他优化手段,如避免不必要的计算和使用 Watcher 进行细粒度控制,从而构建高效、稳定的 Vue 应用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • vue请求接口并且携带token的实现

    vue请求接口并且携带token的实现

    本文主要介绍了vue请求接口并且携带token的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • vue+koa2实现session、token登陆状态验证的示例

    vue+koa2实现session、token登陆状态验证的示例

    这篇文章主要介绍了vue+koa2实现session、token登陆状态验证的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Vue路由组件传递参数的六种场景

    Vue路由组件传递参数的六种场景

    在Vue应用程序中,路由组件是构建单页应用的关键部分,传递参数给路由组件可以让我们动态地展示内容,处理用户输入,以及实现各种交互功能,本文就给大家介绍了六种Vue路由组件传递参数场景,需要的朋友可以参考下
    2023-09-09
  • Vue实现拖拽改变列表顺序详解

    Vue实现拖拽改变列表顺序详解

    这篇文章主要为大家详细介绍了Vue实现拖拽改变列表顺序的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • vue3全局导入bootstrap5方式

    vue3全局导入bootstrap5方式

    这篇文章主要介绍了vue3全局导入bootstrap5方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • vue3简易实现proxy代理实例详解

    vue3简易实现proxy代理实例详解

    这篇文章主要为大家详细介绍了Python实现学生成绩管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • vue-cli脚手架创建项目遇到的坑及解决

    vue-cli脚手架创建项目遇到的坑及解决

    这篇文章主要介绍了vue-cli脚手架创建项目遇到的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • vuex如何在非组件中调用mutations方法

    vuex如何在非组件中调用mutations方法

    这篇文章主要介绍了vuex如何在非组件中调用mutations方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Vue Element Sortablejs实现表格列的拖拽案例详解

    Vue Element Sortablejs实现表格列的拖拽案例详解

    这篇文章主要介绍了Vue Element Sortablejs实现表格列的拖拽案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • 基于vue+axios+lrz.js微信端图片压缩上传方法

    基于vue+axios+lrz.js微信端图片压缩上传方法

    这篇文章主要介绍了基于vue+axios+lrz.js微信端图片压缩上传方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06

最新评论