计算属性:优化 Vue 中的数据操作

一、前言

在本文中,我们将探讨 Vue 中的计算属性(Computed Properties),这是一种强大的数据绑定方式,用于处理响应式数据的复杂逻辑。我们将从介绍计算属性的基本概念开始,然后深入讨论如何定义、使用以及最佳实践。

备注:什么是响应式数据?

Vue 中的数据是响应式的,这意味着当数据发生变化时,相关的视图会自动更新以反映这些变化。计算属性和侦听器等都建立在响应式数据的基础上。

二、内容

2.1 什么是计算属性

计算属性是 Vue 中一种特殊的数据绑定方式,它可以帮助我们以更灵活的方式展示数据,并且可以控制界面的可见性和交互性。

计算属性是依赖于其他数据的属性,当这些依赖数据发生变化时,计算属性会自动更新,无需手动触发。其主要作用是提供多种不同的数据表现方式,同时保持原始数据的不变,以及更便捷地处理界面逻辑。

推荐使用计算属性来处理依赖响应式状态的复杂逻辑,以提高代码可维护性和可读性。

2.2 计算属性的应用场景

计算属性的应用场景包括但不限于:

  • 数据过滤和排序:根据一组数据生成过滤或排序后的新数据。
  • 格式化数据:将原始数据格式化为可读的字符串。
  • 动态类绑定:根据某些条件动态绑定CSS类。
  • 条件渲染:根据条件决定是否渲染特定的元素或组件。

2.3 定义计算属性

在Vue中,我们可以通过 computed 选项来定义计算属性。下面是一个示例:

html 复制代码
<script>
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  }
}
</script>

<template>
	<p>Has published books:</p>
	<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
</template>

比如上述模板中,我们要根据 author 是否已有一些书籍来展示 'Yes''No'。那么,我们可以使用 计算属性 来描述依赖响应式状态的复杂逻辑。

代码重构如下:

html 复制代码
<script>
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    publishedBooksMessage() {
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
</script>

<template>
	<p>Has published books:</p>
	<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
</template>

可以看到,我们定义了一个计算属性 publishedBooksMessage ,当我们更改应用 data 中的 books 数组的值后,可以看到 publishedBooksMessage 会随之改变。

在模板中使用计算属性的方式和一般的属性并无二致。Vue 会检测到 this.publishedBooksMessage 依赖于 this.author.books,所以当 this.author.books 改变时,任何依赖于 this.publishedBooksMessage 的绑定都将同时更新。

2.4 计算属性 vs 方法

你可能会问,计算属性和方法在处理数据时得到的结果是一样的,那它们有什么区别呢?

区别在于计算属性是缓存的。这意味着只有当依赖的数据发生变化时,计算属性才会重新计算。只要依赖属性没有发生变化,多次访问计算属性都只会计算一次,返回之前的计算结果。这对性能优化非常有帮助。

方法(通过methods选项定义)则不会被缓存,每次调用方法都会执行一次函数,即使结果与之前相同。

因此,如果你有一个需要频繁访问的表达式 ,且结果基于其他响应式数据,使用计算属性会更有效率。

记录:

计算属性值会基于其响应式依赖被缓存 。一个计算属性仅会在其响应式依赖更新时才重新计算。而方法调用总是会在重渲染发生时再次执行函数。

2.5 可写计算属性

默认情况下,计算属性是只读的。如果你需要修改计算属性的值,可以提供一个getter和setter:

html 复制代码
<template>
  <div>
    <p>Full Name:</p>
    <input v-model="fullName" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      // getter
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}
</script>

现在,当你修改fullName时,setter会被调用,更新firstNamelastName

注意事项:

  • 当提供了 setter 时,必须同时提供 getter
  • 计算属性的 setter 只能接受一个参数,即新的值。

2.6 最佳实践

Getter 不应有副作用

这意味着计算属性的getter函数应该是纯粹的计算,不应该执行会产生副作用的操作,比如异步请求或更改DOM操作。计算属性的主要目的是根据现有的数据计算新的数据,而不是触发其他操作。如果需要在数据变化时执行副作用操作,应该使用侦听器(watcher)来处理。

因此我们总结如下:

  1. 不要进行异步请求:在计算属性的 getter 中,不应该发起网络请求或执行异步操作,因为这些操作可能需要一些时间来完成,并且会导致计算属性无法立即返回其值。这可能会引发一系列问题,如页面渲染不一致或性能问题。
  2. 不要直接修改数据:计算属性的 getter 应该只用于计算新的数据,而不应该修改组件的数据或状态。直接在计算属性中修改数据可能会导致不可预测的行为,因为它们可能会触发数据变化的循环或其他副作用。
  3. 不要执行影响DOM的操作:在计算属性的 getter 中,不应该进行直接的DOM操作,如更改元素的样式或属性。这种操作应该在Vue.js的生命周期钩子或方法中执行。

计算属性应该专注于计算新的派生数据,而不应该改变组件的状态或应用程序的行为。

避免直接修改计算属性值

计算属性的值应该被视为只读,不应该直接修改它们。如果需要更新计算属性的值,应该通过修改它们所依赖的源状态来触发新的计算。这确保了计算属性的值总是基于最新的源数据计算而来。

举个例子。

假设你有一个 Vue 组件,其中有一个计算属性 total,它计算了两个数据属性 pricequantity 的乘积:

js 复制代码
data() {
  return {
    price: 10,
    quantity: 2
  };
},
computed: {
  total() {
    return this.price * this.quantity;
  }
}

在这种情况下,total 是一个计算属性,它的值依赖于 pricequantity 的值。你可以在模板中使用 {{ total }} 来显示它,但是不应该像这样尝试直接修改它的值:

js 复制代码
this.total = 20; // 不应该这样做

为什么不应该这样做呢?

因为 total 是一个计算属性,它的值是由Vue自动计算的,基于它所依赖的数据属性 pricequantity。如果你直接修改 total 的值,Vue将无法跟踪到这个变化,因为它无法知道你已经手动修改了 total。这可能导致不一致性和意想不到的行为。

相反,如果你想更新 total 的值,应该通过修改它所依赖的源数据属性,即 pricequantity,然后让Vue.js自动重新计算 total。例如:

js 复制代码
this.price = 20; // 修改price
this.quantity = 3; // 修改quantity

当你修改 pricequantity 时,Vue.js将自动重新计算 total,并确保 total 的值是正确的。这就是避免直接修改计算属性值的原因,以保持数据的一致性和Vue.js的响应性。计算属性的主要作用是根据源数据计算出一个派生的值,而不是作为普通属性进行手动修改。

2.7 简单案例

假设我们有一个在线商城的商品列表,用户可以根据价格范围、类别等条件筛选商品。我们可以使用计算属性来动态生成筛选后的商品列表,以及计算出满足条件的商品数量。

html 复制代码
<template>
  <div>
    <div>
      <label for="minPrice">Min Price: </label>
      <input id="minPrice" v-model="filters.minPrice" type="number" />
    </div>
    <div>
      <label for="maxPrice">Max Price: </label>
      <input id="maxPrice" v-model="filters.maxPrice" type="number" />
    </div>
    <div>
      <label for="category">Category: </label>
      <select id="category" v-model="filters.category">
        <option value="All">All</option>
        <option value="food">food</option>
        <option value="Clothing">Clothing</option>
      </select>
    </div>
    <p>Filtered Product Count: {{ filteredProductCount }}</p>
    <ul>
      <li v-for="product in filteredProducts" :key="product.name">
        {{ product.name }} - {{ product.price }} - {{ product.category }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      products: [
        { name: 'Product A', price: 20, category: 'food' },
        { name: 'Product B', price: 30, category: 'Clothing' },
        { name: 'Product C', price: 63, category: 'food' },
        { name: 'Product D', price: 25, category: 'food' },
        { name: 'Product E', price: 56, category: 'Clothing' },
        { name: 'Product F', price: 10, category: 'food' },
        { name: 'Product G', price: 90, category: 'Clothing' },
        { name: 'Product H', price: 22, category: 'food' },
        { name: 'Product I', price: 76, category: 'Clothing' },
        { name: 'Product J', price: 45, category: 'food' },
      ],
      filters: {
        minPrice: 0,
        maxPrice: 100,
        category: 'All',
      },
    };
  },
  computed: {
    filteredProducts() {
      return this.products.filter((product) => {
        const priceInRange =
          product.price >= this.filters.minPrice &&
          product.price <= this.filters.maxPrice;
        const categoryMatch =
          this.filters.category === 'All' ||
          this.filters.category === product.category;
        return priceInRange && categoryMatch;
      });
    },
    filteredProductCount() {
      return this.filteredProducts.length;
    },
  },
}
</script>

在上述示例中,我们使用计算属性 filteredProducts 根据用户的筛选条件动态生成商品列表,并使用 filteredProductCount 计算满足条件的商品数量。这样,用户可以根据不同条件快速筛选商品。

三、总结

综上所述,计算属性是 Vue 中强大的特性,它允许你根据响应式数据动态计算属性的值,提高了代码的可维护性和性能。了解计算属性的工作原理并遵循最佳实践,将帮助你更好地利用 Vue 的数据绑定能力。

相关推荐
一 乐3 小时前
学籍管理平台|在线学籍管理平台系统|基于Springboot+VUE的在线学籍管理平台系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
小御姐@stella3 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
万叶学编程6 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
积水成江9 小时前
关于Generator,async 和 await的介绍
前端·javascript·vue.js
计算机学姐10 小时前
基于SpringBoot+Vue的高校运动会管理系统
java·vue.js·spring boot·后端·mysql·intellij-idea·mybatis
老华带你飞10 小时前
公寓管理系统|SprinBoot+vue夕阳红公寓管理系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot·课程设计
qbbmnnnnnn10 小时前
【WebGis开发 - Cesium】如何确保Cesium场景加载完毕
前端·javascript·vue.js·gis·cesium·webgis·三维可视化开发
杨荧12 小时前
【JAVA开源】基于Vue和SpringBoot的水果购物网站
java·开发语言·vue.js·spring boot·spring cloud·开源
霸王蟹13 小时前
Vue3 项目中为啥不需要根标签了?
前端·javascript·vue.js·笔记·学习
老章学编程i13 小时前
Vue工程化开发
开发语言·前端·javascript·vue.js·前端框架