Обчислювані властивості
Простий приклад
Вирази, вбудовані в шаблон є досить зручними, але їх слід використовувати лише для простих дій. Винесення великої кількості логічних операцій до шаблонів може їх "роздути" та ускладнити подальше обслуговування. Наприклад, у нас є об'єкт з вкладеним масивом:
js
const author = reactive({
name: 'Тарас Шевченко',
books: [
'1840 — Кобзар',
'1851 — Тополя',
'1859 — Заповіт'
]
})
І ми хочемо вивести різні повідомлення, виходячи з того, чи author
має якісь книги, чи ні:
template
<p>Має опубліковані книги:</p>
<span>{{ author.books.length > 0 ? 'Так' : 'Ні' }}</span>
Як бачите, шаблон стає дещо захаращеним. Якщо на секунду поглянути на нього, то буде зрозуміло, що він виконує певне обчислення, яке залежить від author.books
. Ба більше, ми напевне не хочемо повторювати самих себе, якби нам потрібно було скористатись цим обчисленням в шаблоні більше, ніж один раз.
Ось чому наша складна логіка, яка містить в собі реактивні дані, рекомендована для використання в обчислюваній властивості. Ось той самий приклад, але відрефакторений:
vue
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'Тарас Шевченко',
books: [
'1840 — Кобзар',
'1851 — Тополя',
'1859 — Заповіт'
]
})
// обчислювана властивість - ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Так' : 'Ні'
})
</script>
<template>
<p>Має опубліковані книги:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Тут ми оголосили обчислювану властивість publishedBooksMessage
. Функція computed()
очікує в якості аргументу функцію, яка повертає якесь значення. Результат виконання функції computed()
є обчислювана референція. Як і звичайні референції, ви можете отримати до їхнього обчисленого результату через publishedBooksMessage.value
. Обчислювані референції є також такими, що автоматично розпаковуються в шаблонах, щоб ви могли їх використовувати без .value
у шаблонних виразах.
Обчислювана властивість автоматично відстежує свої реактивні залежності. Vue знає, що обчислення publishedBooksMessage
залежить від author.books
, тому він оновить усі зв'язки publishedBooksMessage
, у разі якщо author.books
зміниться.
Також перегляньте: Типізовані обчислювані вирази
Кешування обчислюваних виразів та методи
Ви могли помітити, що ми можемо досягнути такий ж самий результат просто виконавши метод, представлений у вигляді виразу:
template
<p>{{ calculateBooksMessage() }}</p>
js
// в компоненті
function calculateBooksMessage() {
return author.books.length > 0 ? 'Так' : 'Ні'
}
Замість обчислюваної властивості ми можемо оголосити таку ж функцію у якості метода. В кінцевому результаті, два цих підходи точнісінько такі ж самі. Але різниця між ними полягає в тому, що обчислювані властивості кешуються на основі їхніх реактивних залежностей. Тобто, обчислювана властивість перерахується лише в тому випадку, коли її хоч якась реактивна залежність змінюється. Це означає, що скільки б разів ми б не звертались до publishedBooksMessage
, вона не буде обчислюватись повторно, а буде повертатись результат, обчислений перед цим, аж поки author.books
не зміниться.
Це також означає, що наступна обчислювана властивість ніколи не оновиться, оскільки Date.now()
не є реактивною залежністю:
js
const now = computed(() => Date.now())
Для порівняння, виклик метода завжди виконуватиме цю функцію, аж поки не відбудеться повторний рендерінг.
Навіщо нам кешування? Уявіть, що у нас є "дорога" обчислювана властивість list
, яка вимагає циклічного перегляду величезного масиву та виконання великої кількості обчислень. Тоді ми можемо мати інші обчислені властивості, які, своєю чергою, залежать від list
. Без кешування ми б виконували геттер для list
набагато більше разів, ніж потрібно! Отже, у випадках, коли ви не бажаєте кешування, замість цього використовуйте виклик методу.
Обчислювана властивість з можливістю запису
Обчислювані властивості за замовчуванням призначені лише для читання. Якщо ви спробуєте призначити нове значення обчислюваній властивості, ви отримаєте попередження під час виконання. У рідкісних випадках, коли вам потрібна «записувана» обчислювана властивість, ви можете створити її, надавши як getter, так і 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>
Тепер, коли ви запускаєте fullName.value = 'Тарас Шевченко'
, буде викликано setter і firstName
та lastName
будуть відповідно оновлені.
Рекомендації
Геттери не мають мати побічних ефектів
Важливо пам'ятати, що обчислювані функції повинні виконувати лише чисті (pure functions) обчислення та не мати побічних ефектів. Наприклад, не робіть асинхронні запити та не змінюйте DOM в обчислюваному геттері! Подумайте про обчислювану властивість як про декларативний опис того, як отримати значення на основі інших значень – її єдиною відповідальністю має бути обчислення та повернення цього значення. Далі в Гіді ми обговоримо, як ми можемо виконувати побічні ефекти у відповідь на зміни стану за допомогою спостерігачів.
Уникайте змін обчисленого значення
Значення, що повертається з обчислюваної властивості, є похідним станом. Подумайте про це як про тимчасовий знімок – кожного разу, коли вихідний стан змінюється, створюється новий знімок. Немає сенсу змінювати знімок, тому обчислене значення, що повертається, слід розглядати як лише для читання та ніколи не змінювати – натомість оновіть вихідний стан, від якого воно залежить, щоб ініціювати нові обчислення.