[VUE3] Computed properties and their caching characteristics

Computed properties

Basic example

Although expressions in templates are convenient, they can only be used for simple operations. If you write too much logic in a template, it will become bloated and difficult to maintain. For example, we have an object containing nested arrays:

const author = reactive({
    
    
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

We want to display different information based on whether the author already has some books:

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

The template here looks a bit complicated. We have to look hard for a while to understand that its calculation depends on author.books. More importantly, if more than one such calculation is needed in the template, we don't want to repeat this code multiple times in the template.

Therefore we recommend using computed properties to describe complex logic that relies on reactive state. Here is the refactored example:

vue
<script setup>
import {
      
       reactive, computed } from 'vue'

const author = reactive({
      
      
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
      
      
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{
   
   { publishedBooksMessage }}</span>
</template>

Try it out on the training ground

We define a computed property publishedBooksMessage here. The computed() method expects to receive a getter function and the return value is a computed attribute ref . Like other general refs, you can access the calculation results through publishedBooksMessage.value. Computed property refs are also automatically unwrapped in templates, so there is no need to add .value when referencing them in template expressions .
Insert image description here
training ground

Vue’s computed properties automatically track reactive dependencies. It will detect that publishedBooksMessage depends on author.books, so when author.books changes, any bindings that depend on publishedBooksMessage will be updated at the same time. On the contrary, if author.books remains unchanged, the calculated property will not be triggered.

Computed property cache vs method

You may notice that we get the same result as a computed property when we call a function like this in an expression:

template
<p>{
    
    {
    
     calculateBooksMessage() }}</p>
js
// 组件中
function calculateBooksMessage() {
    
    
  return author.books.length > 0 ? 'Yes' : 'No'
}

If we define the same function as a method instead of a computed property, the results are exactly the same, however, the difference is that the computed property value is cached based on its reactive dependencies. A computed property is only recalculated when its reactive dependencies are updated. This means that as long as author.books does not change, no matter how many times you access publishedBooksMessage, the previous calculation result will be returned immediately without repeatedly executing the getter function.

This also explains why the following computed property never updates, because Date.now() is not a reactive dependency:

js
const now = computed(() => Date.now())

Insert image description here
training ground

In contrast, method calls always execute the function again when a re-render occurs.

Why do you need caching? Imagine we have a very performance-intensive list of computed properties that requires looping over a huge array and doing a lot of calculation logic, and there may be other computed properties that depend on the list. Without caching, we would repeat the getter of the list many times, but this is actually unnecessary! If you are sure you don't need caching, you can also use method calls.

Writable computed properties

Computed properties are read-only by default. When you try to modify a computed property, you will receive a runtime warning. Only in some special cases you may need to use "writable" properties, which you can create by providing both a getter and a setter:

vue
<script setup>
import {
      
       ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
      
      
  // getter
  get() {
      
      
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
      
      
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

Now when you run fullName.value = 'John Doe', the setter will be called and firstName and lastName will be updated.

Best Practices

Getter should not have side effects

It is very important to remember that the getter of a computed property should only do the calculation without any other side effects. For example, don't make asynchronous requests or change the DOM in getters! The declaration of a computed property describes how to derive a value from other values. So the getter's responsibility should only be to compute and return the value. In a later tutorial we'll discuss how to use listeners to create side effects based on changes to other reactive state.

Avoid directly modifying calculated property values

The value returned from a computed property is derived state. Think of it as a "temporary snapshot", a new snapshot is created every time the source state changes. There is no point in changing the snapshot, so the return value of a computed property should be considered read-only and should never be changed - the source state it depends on should be updated to trigger a new calculation.

Guess you like

Origin blog.csdn.net/weixin_43361722/article/details/129817937