11 Vue3中的computed计算属性

2023-12-17 13:32:08

概述

Computed properties are unique data types that will reactively update only when the source data used within the property is updated. By defining a data property as a computed property, we can perform the following activities:

  • Apply custom logic on the original data property to calculate the computed property’s value
  • Track the changes of the original data property to calculate the updated value of the computed property
  • Reuse the computed property as local data anywhere within the Vue component

计算属性是一种独特的数据类型,只有在属性中使用的源数据更新时才会被动更新。通过将数据属性定义为计算属性,我们可以执行以下活动:

  • 在原始数据属性上应用自定义逻辑,计算计算属性的值

  • 跟踪原始数据属性的变化,计算计算属性的更新值

  • 在 Vue 组件中的任何位置将计算属性作为本地数据重复使用

By default, the Vue engine automatically caches the computed properties, making them more performant at updating the UI than using the property of the returned value of data, or using a Vue component’s method.

默认情况下,Vue 引擎会自动缓存计算属性,这使得它们在更新 UI 时比使用数据返回值的属性或使用 Vue 组件的方法更高效。

The syntax of a computed property is like writing a component method with a return value, nested under the computed property of the Vue component:

计算属性的语法就像编写一个带有返回值的组件方法,嵌套在 Vue 组件的计算属性下:

<script>
export default {
  computed: {
    yourComputedProperty() {
      /* need to have return value */
    }
  }
}
</script>

Within the computed property’s logic, you can access any component’s data property, method, or other computed property using the this instance, which is the reference to the Vue component instance itself. An example of using the this instance is shown here:

在计算属性的逻辑中,你可以使用 this 实例访问任何组件的数据属性、方法或其他计算属性,this 实例是对 Vue 组件实例本身的引用。下面是一个使用 this 实例的示例:

<script>
export default {
  data() {
    return {
      yourData: "your data"
    }
  },
  computed: {
    yourComputedProperty() {
      return `${this.yourData}-computed`;
    }
  }
}
</script>

计算属性的使用场景

Let’s look at some examples of where you should consider using a computed property.

让我们举例说明在哪些情况下应考虑使用计算属性。

表单校验

In the following example, we have an input field, which attaches to the name data property, and error is a computed property. If name contains a falsy value (which means name is an empty string, 0, undefined, null, or false), error will be assigned a value of “Name is required”. Otherwise, it will be empty.

在下面的示例中,我们有一个与 name 数据属性相连的输入字段,而 error 是一个计算属性。如果 name 包含虚假值(即 name 为空字符串、0、未定义、空或 false),error 将被赋值为 “Name is required”。否则,该值将为空。

The component then renders the value of the error property accordingly:

然后,组件会相应地渲染错误属性的值:

<template>
  <input v-model="name">
  <div>
    <span>{{ error }}</span>
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: '',
    }
  },
  computed: {
    error() {
      return this.name ? '' : 'Name is required'
    }
  }
}
</script>

合并数据属性

You can use computed props to combine multiple data properties to generate a single computed property. Take the following code for instance. We combine two pieces of data – title and surname – into one computed string, formalName, and render its value using template:

你可以使用计算道具来组合多个数据属性,生成一个计算属性。以下面的代码为例。我们将标题和姓氏这两个数据合并为一个计算字符串 formalName,并使用模板呈现其值:

<template>
  <div>{{ formalName }}</div>
</template>
<script>
export default {
  data() {
    return {
      title: 'Mr.',
      surname: 'Smith'
    }
  },
  computed: {
    formalName() {
      return `${this.title} ${this.surname}`;
    }
  }
}
</script>

计算和显示复杂信息

Sometimes there is a need to perform an extra calculation or to extract specific information from one large data object source. Computed properties help to achieve this goal while keeping our code readable.

有时需要执行额外的计算,或从一个大型数据对象源中提取特定信息。计算属性有助于实现这一目标,同时保持代码的可读性。

Take a large data object, such as post. This data object has a nested fields property, which contains several additional information objects, such as the full name of author and an array of entries objects. Each entry in entries contains further information, such as title, content, and a feature flag indicating whether the entry should be featured:

以一个大型数据对象为例,如 post。该数据对象有一个嵌套字段属性,其中包含多个附加信息对象,如作者全名和条目对象数组。条目中的每个条目都包含更多信息,如标题、内容和表示条目是否应被突出显示的特征标志:

<script>
export default {
  data() {
    return {
      post: {
        fields: {
          author: {
            firstName: 'John',
            lastName: 'Doe'
          },
          entries: [{
            title: "Entry 1",
            content: "Entry 1's content",
            featured: true
          },
            {
              title: "Entry 2",
              content: "Entry 2's content",
              featured: false
            }]
        }
      }
    }
  },
}
</script>

In this scenario, you need to perform the following steps:

  • Display the full name of the post’s author.
  • Calculate and display the total number of entries included.
  • Display a list of entries that have the feature flag turned on (feature: true).

在这种情况下,您需要执行以下步骤:

  • 显示帖子作者的全名。
  • 计算并显示包含的条目总数。
  • 显示已打开特征标志(特征:true)的条目列表。

By using computed properties, we can decouple the previous post object into several computed data properties while keeping the original post object unchanged, as follows.

通过使用计算属性,我们可以将之前的文章对象解耦为多个计算数据属性,同时保持原始文章对象不变,如下所示。

<script>
export default {
  data() {
    return {
      post: {
        fields: {
          author: {
            firstName: 'John',
            lastName: 'Doe'
          },
          entries: [{
            title: "Entry 1",
            content: "Entry 1's content",
            featured: true
          },
            {
              title: "Entry 2",
              content: "Entry 2's content",
              featured: false
            }]
        }
      }
    }
  },
  computed: {
    // fullName 用于合并 post.fields.author 的名和姓:
    fullName() {
      const {firstName, lastName} =
          this.post.fields.author;
      return `${firstName} ${lastName}`
    },
    // totalEntries 包含 post.fields.entries 数组的长度:
    totalEntries () {
      return this.post.fields.entries.length
    },
    // featuredEntries 包含根据每个条目的特征属性过滤后的 post.fields.entries 列表,使用的是 filter 内置数组方法:
    featuredEntries() {
      const { entries } = this.post.fields;
      return entries.filter(entry => !!entry.featured)
    }
  }
}
</script>

You then use the simplified and semantic computed properties to render the information in your component’s template.

然后,使用简化和语义计算属性在组件模板中呈现信息。

<template>
  <div>
    <p>{{ fullName }}</p>
    <p>{{ totalEntries }}</p>
    <p>{{ featuredEntries }}</p>
  </div>
</template>

Computed properties are very valuable to Vue developers when creating performant components. In the next exercise, we will explore how to use them inside a Vue component.

在创建高性能组件时,计算属性对 Vue 开发人员非常有价值。在下一个练习中,我们将探讨如何在 Vue 组件中使用它们。

练习:使用计算属性

In this exercise, you will use a computed property to help cut down the amount of code you need to write inside your Vue templates by concisely outputting basic data.

在本练习中,您将使用计算属性,通过简洁地输出基本数据,帮助减少 Vue 模板中需要编写的代码量。

Let’s create a new Vue component called Exercise2-01 by adding the Exercise2-01.vue file to the ./src/components/ folder:

让我们在 ./src/components/ 文件夹中添加 Exercise2-01.vue 文件,创建一个名为 Exercise2-01 的新 Vue 组件:

在App.vue中,引入并使用该组件:

<script setup>
import Exercise from "./components/Exercise2-01.vue";
</script>
<template>
  <Exercise/>
</template>

Open Exercise2-01.vue and let’s create the code block structure for the Vue component, as follows:

打开 Exercise2-01.vue,为 Vue 组件创建代码块结构,如下所示:

<template>
</template>
<script>
export default {
}
</script>

In <template>, create an input field for the first name, and use v-model to bind the data prop, firstName, to this field:

<template> 中,为名字创建一个输入字段,并使用 v-model 将数据道具 firstName 绑定到该字段:

<input v-model="firstName" placeholder="First name" />

Create a second input field for the last name, and use v-model to bind the data prop, lastName, to this field:

为姓氏创建第二个输入字段,并使用 v-model 将数据道具 lastName 与该字段绑定:

<input v-model="lastName" placeholder="Last name" />

Include these new v-model data props in the Vue instance by returning them in the data() function:

通过在 data() 函数中返回这些新的 v 模型数据道具,将其包含在 Vue 实例中:

<script>
export default {
  data() {
    return {
      firstName: '',
      lastName: '',
    }
  },
}
</script>

Create a computed data variable called fullName:

创建名为 fullName 的计算数据变量:

<script>
export default {
  data() {
    return {
      firstName: '',
      lastName: '',
    }
  },
  computed: {
    fullName() {
      return '${this.firstName} ${this.lastName}'
    },
  },
}
</script>

Underneath your input fields, output the computed data using the h3 tag:

在输入字段下方,使用 h3 标签输出计算数据:

<template>
  <input v-model="firstName" placeholder="First name" />
  <input v-model="lastName" placeholder="Last name" />
  <h3 class="output">{{ fullName }}</h3>
</template>

This exercise demonstrates how we can write an expression inside a computed data property using data received from v-model, and then combine the first name and last name into a single output variable with the fullName computed property that can be reused within the component.

本练习演示了如何使用从 v-model 收到的数据在计算数据属性内编写表达式,然后将姓和名与可在组件内重复使用的计算属性 fullName 结合为一个输出变量。

We now understand how a computed property works and how to write a declarative, reusable, and reactive computed property. Next, we will look at how to intercept the mutation process of a computed property and add additional logic with the computed setter feature.

现在,我们了解了计算属性的工作原理,以及如何编写声明式、可重用和反应式的计算属性。接下来,我们将了解如何拦截计算属性的突变过程,并使用计算设置器功能添加附加逻辑。

修改计算属性的值

By default, computed data is a getter only, which means it will only output the outcome of your expression. In some practical scenarios, when a computed property is mutated, you may need to trigger an external API or mutate the original data elsewhere in the project. The function performing this feature is called a setter.

默认情况下,计算数据只是一个 getter,这意味着它只会输出表达式的结果。在某些实际场景中,当计算属性发生变化时,您可能需要触发外部 API 或在项目的其他地方对原始数据进行变化。执行此功能的函数称为设置器。

Using a setter in a computed property allows you to reactively listen to data and trigger a callback (setter) that contains the returned value from the getter, which can optionally be used in the setter.

在计算属性中使用设置器,可以对数据进行反应式监听,并触发一个回调(设置器),其中包含从获取器返回的值,该值可选择用于设置器。

But first, let’s look at JavaScript ES5’s getter and setter. Starting from ES5, you can use the built-in getter and setter to define Object accessors, such as the following:

不过,首先让我们了解一下 JavaScript ES5 的 getter 和 setter。从 ES5 开始,您可以使用内置的 getter 和 setter 来定义对象访问器,例如下面的内容:

get to bind the Object property to a function that returns a value for that property whenever it is looked up, as shown here:

将对象属性绑定到一个函数,该函数会在每次查询时返回该属性的值,如图所示:

const obj = {
  get example() {
    return 'Getter'
  }
}
console.log(obj.example) //Getter

set to bind the specific Object property to a function whenever that property is modified:

设置,以便在修改特定对象属性时将该属性绑定到一个函数:

const obj = {
  set example(value) {
    this.information.push(value)
  },
  information: []
}
obj.example = 'hello'
obj.example = 'world'
console.log(obj.information) //['hello', 'world']

Based on those features, Vue.js provides us with similar functionalities, get() as the getter and set() as the setter, for a specific computed property:

基于这些功能,Vue.js 为我们提供了类似的功能,即针对特定的计算属性,将 get() 作为获取器,将 set() 作为设置器:

<script>
export default {
  computed: {
    myComputedDataProp: {
      get() {},
      set(value) {}
    }
  }
}
</script>

To understand how setter and getter work, let’s perform the following steps:

要了解 setter 和 getter 如何工作,让我们执行以下步骤:

Define the returned value of myComputedDataProp to be this.count + 1 whenever myComputedDataProp is looked up:

定义每次查询 myComputedDataProp 时,myComputedDataProp 的返回值为 this.count + 1:

<script>
export default {
  computed: {
    myComputedDataProp: {
      get() {
        return this.count + 1
      },
      set(value) {}
    }
  }
}
</script>

Then, whenever myComputedDataProp is modified, use the setter to update the count data prop to its new value, and then call a method within the component called callAnotherApi with this new this.count value:

然后,每当修改 myComputedDataProp 时,使用设置器将计数数据道具更新为新值,然后使用新的 this.count 值调用组件中名为 callAnotherApi 的方法:

<script>
export default {
  computed: {
    myComputedDataProp: {
      get() {
        return this.count + 1
      },
      set(value) {
        this.count = value - 1
        this.callAnotherApi(this.count)
      }
    }
  }
}
</script>

With count and callAnotherApi is the component’s local data and method, respectively.

其中,count 和 callAnotherApi 分别是组件的本地数据和方法。

The full example code is as follows:

完整的示例代码如下:

<template>
  <h1>原始值:{{ count }}</h1>
  <h1>属性值:{{ myComputedDataProp }}</h1>
  <button @click="myComputedDataProp+=33">修改属性的值</button>
</template>
<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  computed: {
    myComputedDataProp: {
      get() {
        return this.count + 1
      },
      set(value) {
        this.count = value - 1
      },
    },
  },
}
</script>

Here the computed myComputedDataProp prop will output 1 in your Vue component.

在这里,计算后的 myComputedDataProp 将在 Vue 组件中输出 1。

You will find out exactly how to use computed data as both getters and setters in the following exercise.

您将在下面的练习中了解如何将计算数据作为获取器和设置器使用。

练习:修改计算属性值

In this exercise, you will use a computed prop as a setter and a getter, which will both output expressions and set data when triggered by a user’s input.

在本练习中,你将使用一个计算道具作为设置器和获取器,当用户输入触发时,它将同时输出表达式和设置数据。

Let’s create a new Vue component called Exercise2-02 by adding the Exercise2-02.vue file to the ./src/components/ folder.

让我们在 ./src/components/ 文件夹中添加 Exercise2-02.vue 文件,创建一个名为 Exercise2-02 的新 Vue 组件。

修改App.vue,引入该组件并使用:

<script setup>
import Exercise from "./components/Exercise2-02.vue";
</script>
<template>
  <Exercise/>
</template>

Open Exercise2-02.vue and let’s create the code block structure for the Vue component, as follows:

打开 Exercise2-02.vue,创建 Vue 组件的代码块结构如下:

<template>
</template>
<script>
export default {}
</script>

Create an input field with a v-model value bound to a computed data value called incrementOne, return the value of a Vue data variable called count in the getter, and set the count variable in the setter:

创建一个输入字段,其 v 模型值绑定到名为 incrementOne 的计算数据值,在 getter 中返回名为 count 的 Vue 数据变量的值,并在 setter 中设置 count 变量:

<template>
  <div class="container">
    <input type="number" v-model="incrementOne" />
    <h3>Get input: {{ incrementOne }}</h3>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: -1,
    }
  },
  computed: {
    incrementOne: {
      get() {
        return this.count + 1
      },
      set(val) {
        this.count = val - 1
      },
    },
  },
}
</script>

Next, let’s utilize the setter again. We will divide whatever the new val argument is by 2, and save that to a new data variable called divideByTwo:

接下来,让我们再次使用 setter。我们将用新的 val 参数除以 2,并将其保存到一个名为 divideByTwo 的新数据变量中:

<template>
  <div class="container">
    <input type="number" v-model="incrementOne" />
    <h3>Get input: {{ incrementOne }}</h3>
    <h5>Set division: {{ divideByTwo }}</h5>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: -1,
      divideByTwo: 0,
    }
  },
  computed: {
    incrementOne: {
      get() {
        return this.count + 1
      },
      set(val) {
        this.count = val - 1
      },
    },
  },
}
</script>

Update the setter to divide val by 2, and bind this new value to the divideByTwo variable:

更新设置器,将 val 除以 2,并将新值绑定到 divideByTwo 变量:

<template>
  <div class="container">
    <input type="number" v-model="incrementOne"/>
    <h3>Get input: {{ incrementOne }}</h3>
    <h5>Set division: {{ divideByTwo }}</h5>
  </div>
</template>
<script>
export default {
  data() {
    return {
      count: -1,
      divideByTwo: 0,
    }
  },
  computed: {
    incrementOne: {
      get() {
        return this.count + 1
      },
      set(val) {
        this.count = val - 1
        this.divideByTwo = val / 2
      },
    },
  },
}
</script>

This exercise demonstrates how we can use computed data to both get and set data reactively in our template by binding computed variables to the vmodel.

本练习演示了如何通过将计算变量绑定到 vmodel,在模板中使用计算数据来获取和设置数据。

In the next section, we will explore how we can use watchers to actively listen to changes in component data or its property.

在下一节中,我们将探讨如何使用观察者来主动监听组件数据或其属性的变化。

文章来源:https://blog.csdn.net/qq_37703224/article/details/135043584
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。