Вычисляемые свойства
Простой пример
Выражения внутри шаблона удобны, но предназначены для простых операций. Большое количество логики в шаблоне сделает его раздутым и сложным для поддержки. Например, если есть объект с вложенным массивом:
js
const author = reactive({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] })
И потребуется отображать разные сообщения, в зависимости от того, есть ли у author
какие-то книги или нет:
template
<p>Есть опубликованные книги:</p> <span>{{ author.books.length > 0 ? 'Да' : 'Нет' }}</span>
В таком случае шаблон уже не будет простым и декларативным. Потребуется взглянуть на него, прежде чем понять, что он выполняет вычисления в зависимости от author.books
. Проблема усугубится, если подобные вычисления в шаблоне потребуются не один раз.
Поэтому для сложной логики, включающей реактивные данные, следует использовать вычисляемые свойства.
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 ? 'Да' : 'Нет' }) </script> <template> <p>Есть опубликованные книги:</p> <span>{{ publishedBooksMessage }}</span> </template>
В этом примере объявляем новое вычисляемое свойство publishedBooksMessage
. Функция computed()
ожидает передачи функции-геттера и возвращает значение в виде ref вычисляемого свойства. Подобно обычным ref-ссылкам, можно получить доступ к вычисленному результату через publishedBooksMessage.value
. Вычисляемые свойства в виде ref-ссылок также автоматически разворачиваются в шаблонах, поэтому можно ссылаться на них без .value
в выражениях шаблонов.
Вычисляемое свойство автоматически отслеживает свои реактивные зависимости. Vue знает, что publishedBooksMessage
зависит от значения author.books
, поэтому будет обновлять все привязки, которые зависят от publishedBooksMessage
, при изменениях author.books
.
См. также: Типизация вычисляемых свойств
Кэширование вычисляемых свойств vs. Методы
Можно заметить, что того же результата можно достичь с помощью метода в выражении:
template
<p>{{ calculateBooksMessage() }}</p>
js
// в компоненте function calculateBooksMessage() { return author.books.length > 0 ? 'Да' : 'Нет' }
Вместо вычисляемого свойства можно объявить эту же функцию в качестве метода. Для отображаемого результата эти два подхода действительно одинаковы. Однако разница заключается в том, что вычисляемые свойства кэшируются на основе своих реактивных зависимостей. Вычисляемое свойство будет пересчитываться только при изменении одной из своих зависимостей. А значит, пока не изменится author.books
, любое число обращений к вычисляемому свойству publishedBooksMessage
будет немедленно возвращать ранее вычисленный результат, без необходимости повторного запуска функции-геттера.
Это также означает, что следующее вычисляемое свойство никогда не будет обновляться, потому что Date.now()
не является реактивной зависимостью:
js
// НЕ БУДЕТ РАБОТАТЬ const now = computed(() => Date.now())
Для сравнения, вызов метода будет всегда запускать функцию, когда происходит перерисовка.
Зачем нужно кэширование? Представьте, что есть затратное вычисляемое свойство list
, которому требуется проходить по большому массиву и выполнять различные вычисления. Далее, могут быть другие вычисляемые свойства, которые зависят от значения list
. Без кэширования выполнять геттер list
потребуется во много раз больше, чем это нужно! Когда же необходимо обойтись без кэширования — стоит использовать метод.
Вычисляемое свойство с возможностью записи
Вычисляемые свойства по умолчанию состоят только из геттера. При попытке присвоить ему новое значение будет выброшено предупреждение во время выполнения. В редких случаях, когда требуется вычисляемое свойство «с возможностью записи», можно создать такое, указав и геттер и сеттер:
vue
<script setup> import { ref, computed } from 'vue' const firstName = ref('John') const lastName = ref('Doe') const fullName = computed({ // геттер (для получения значения) get() { return firstName.value + ' ' + lastName.value }, // сеттер (при присвоении нового значения) set(newValue) { // Примечание: это синтаксис деструктурирующего присваивания [firstName.value, lastName.value] = newValue.split(' ') } }) </script>
Теперь, при выполнении fullName.value = 'John Doe'
вызовется сеттер вычисляемого свойства и значения firstName
и lastName
будут соответственно обновлены.
Получение предыдщуего значения
- Поддерживается только в версиях 3.4+
Если вам понадобится, вы можете получить предыдущее значение, возвращенное вычисляемым свойством, обратившись к первому аргументу геттера:
vue
<script setup> import { ref, computed } from 'vue' const count = ref(2) // Это вычисление вернет значение count, если оно меньше или равно 3. // Если count >=4, вместо него будет возвращено последнее значение, выполнившее наше условие, // пока count не станет меньше или равно 3. const alwaysSmall = computed((previous) => { if (count.value <= 3) { return count.value } return previous }) </script>
В случае если вы используете вычисляемое свойство с возможностью записи:
vue
<script setup> import { ref, computed } from 'vue' const count = ref(2) const alwaysSmall = computed({ get(previous) { if (count.value <= 3) { return count.value } return previous }, set(newValue) { count.value = newValue * 2 } }) </script>
Лучшие практики
Геттеры должны быть без побочных эффектов
Важно помнить, что вычисляемые функции геттера должны быть чистыми функциями и не иметь побочных эффектов. Например, не делайте асинхронных запросов и не изменяйте DOM внутри геттера вычисляемого свойства! Думайте о вычисляемом свойстве как о декларативном описании того, как получить значение на основе других значений — его единственной обязанностью должно быть вычисление и возвращение этого значения. Далее в руководстве обсудим, как можно выполнять побочные эффекты в ответ на изменения состояния с помощью методов-наблюдателей watchers.
Избегайте мутации вычисляемого значения
Возвращаемое значение вычисляемого свойства это производное состояние. Считайте его временным снимком — при каждом изменении состояния источника создаётся новый снимок. Не имеет смысла изменять снимок, поэтому вычисляемое возвращаемое значение следует рассматривать как доступное только для чтения и никогда не изменять — вместо этого следует обновить состояние источника, от которого оно зависит, чтобы вызвать вычисление нового значения.