Skip to content

Właściwości obliczane (Computed Properties)

Przykład podstawowy

Wyrażenia w szablonach są bardzo wygodne, ale służą do wykonywania prostych operacji. Umieszczanie zbyt dużej ilości logiki w szablonach może spowodować, że staną się one obszerne i trudne do utrzymania. Na przykład, jeśli mamy obiekt z zagnieżdżoną tablicą:

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

Chcemy też wyświetlać różne komunikaty w zależności od tego, czy autor ma już jakieś książki, czy nie:

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

W tym momencie szablon staje się nieco zagracony. Musimy przyjrzeć się mu przez chwilę, aby zorientować się, że wykonuje on obliczenia w zależności od author.books. Co ważniejsze, prawdopodobnie nie chcemy się powtarzać, jeśli będziemy musieli zawrzeć to obliczenie w szablonie więcej niż jeden raz.

Dlatego właśnie w przypadku złożonej logiki, która zawiera dane reaktywne, zaleca się używanie właściwości computed. Poniżej znajduje się ten sam przykład po refaktoryzacji:

js
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // obliczeniowy getter
    publishedBooksMessage() {
      // `this` wskazuje na instancję komponentu
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
template
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>

Spróbuj tego w Vue Playground

Tutaj zadeklarowaliśmy właściwość obliczaną publishedBooksMessage.

Spróbuj zmienić wartość tablicy books w aplikacji data, a zobaczysz jak publishedBooksMessage odpowiednio się zmienia.

Możesz powiązać dane z właściwościami obliczanymi w szablonach tak samo jak z normalnymi właściwościami. Vue jest świadome, że this.publishedBooksMessage zależy od this.author.books, więc zaktualizuje wszystkie powiązania, które zależą od this.publishedBooksMessage, gdy this.author.books się zmieni.

Zobacz także: Typowanie Computed Properties

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 obliczeniowy
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

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

Spróbuj tego w Vue Playground

Tutaj zadeklarowaliśmy computed property (właściwość obliczaną) publishedBooksMessage. Funkcja computed() oczekuje, że zostanie jej przekazana funkcja pobierająca, a zwróconą wartością jest computed ref. Podobnie jak w przypadku zwykłych refów, możesz uzyskać dostęp do obliczonego wyniku jako publishedBooksMessage.value. Obliczone referencje są również automatycznie rozpakowywane w szablonach, więc można się do nich odwoływać bez .value w wyrażeniach szablonów.

Obliczona właściwość automatycznie śledzi swoje reaktywne zależności. Vue jest świadome, że obliczenie publishedBooksMessage zależy od author.books, więc zaktualizuje wszystkie powiązania, które zależą od publishedBooksMessage, gdy author.books się zmieni.

Zobacz także: Typowanie Computed Properties

Buforowanie Computed a metody

Być może zauważyłeś, że ten sam rezultat możemy osiągnąć, wywołując metodę w wyrażeniu:

template
<p>{{ calculateBooksMessage() }}</p>
js
// w komponencie
methods: {
  calculateBooksMessage() {
    return this.author.books.length > 0 ? 'Yes' : 'No'
  }
}
js
// w komponencie
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Yes' : 'No'
}

Zamiast właściwości obliczanej możemy zdefiniować tę samą funkcję jako metodę. Jeśli chodzi o wynik końcowy, oba podejścia są w istocie dokładnie takie same. Różnica polega jednak na tym, że właściwości obliczeniowe są buforowane w oparciu o ich reaktywne zależności. Właściwość obliczeniowa będzie ponownie oceniana tylko wtedy, gdy niektóre z jej reaktywnych zależności ulegną zmianie. Oznacza to, że dopóki author.books nie ulegnie zmianie, wielokrotny dostęp do publishedBooksMessage natychmiast zwróci poprzednio obliczony wynik bez konieczności ponownego uruchamiania funkcji gettera.

Oznacza to również, że poniższa właściwość obliczeniowa nigdy się nie zaktualizuje, ponieważ Date.now() nie jest zależnością reaktywną:

js
computed: {
  now() {
    return Date.now()
  }
}
js
const now = computed(() => Date.now())

Dla porównania, wywołanie metody spowoduje zawsze uruchomienie funkcji, gdy tylko nastąpi renderowanie.

Dlaczego potrzebujemy buforowania? Wyobraźmy sobie, że mamy kosztowną właściwość obliczeniową list, która wymaga zapętlenia ogromnej tablicy i wykonania wielu obliczeń. Następnie możemy mieć inne obliczane właściwości, które z kolei zależą od listy. Bez buforowania, wykonywalibyśmy getter list o wiele więcej razy niż to konieczne! W przypadkach, gdy nie chcesz buforować, użyj zamiast tego wywołania metody.

Zapisywalne Computed

Właściwości obliczane są domyślnie typu getter-only. Jeśli spróbujesz przypisać nową wartość do właściwości obliczeniowej, otrzymasz ostrzeżenie w czasie wykonywania. W rzadkich przypadkach, gdy potrzebna jest "zapisywalna" właściwość obliczana, można ją utworzyć, podając zarówno getter, jak i setter:

js
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      // getter
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
        // Uwaga: używamy tutaj składni przypisania destrukturyzacji.
        ;[this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}

Teraz, gdy uruchomisz this.fullName = 'John Doe', setter zostanie wywołany, a this.firstName i this.lastName zostaną odpowiednio zaktualizowane.

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) {
    // Uwaga: używamy tutaj składni przypisania destrukturyzacji.
    ;[firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

Teraz, gdy uruchomisz this.fullName = 'John Doe', setter zostanie wywołany, a this.firstName i this.lastName zostaną odpowiednio zaktualizowane.

Najlepsze praktyki

Gettery powinny być wolne od efektów ubocznych

Ważne jest, aby pamiętać, że obliczeniowe funkcje pobierające powinny wykonywać tylko czyste obliczenia i być wolne od efektów ubocznych. Na przykład, nie wykonuj asynchronicznych żądań ani nie mutuj DOM wewnątrz obliczeniowego gettera! Pomyśl o właściwości computed jako o deklaratywnym opisie sposobu uzyskiwania wartości na podstawie innych wartości - jej jedynym zadaniem powinno być obliczenie i zwrócenie tej wartości. W dalszej części przewodnika omówimy, w jaki sposób możemy wykonywać efekty uboczne w reakcji na zmiany stanu za pomocą watchers.

Unikaj modyfikacji wartości obliczanej

Wartość zwracana przez obliczaną właściwość jest stanem pochodnym. Pomyśl o nim jak o tymczasowej migawce - za każdym razem, gdy zmienia się stan źródłowy, tworzona jest nowa migawka. Modyfikowanie migawki nie ma sensu, dlatego wartość zwracana przez właściwość obliczaną powinna być traktowana jako tylko do odczytu i nigdy nie powinna być modyfikowana - zamiast tego należy zaktualizować stan źródłowy, od którego zależy, aby wywołać nowe obliczenia.

Właściwości obliczane (Computed Properties) has loaded