Vue

computed: производные значения

В корзине интернет-магазина итоговая сумма зависит от списка товаров, скидки и налога. Можно написать функцию-метод и вызывать её в шаблоне в трёх местах. Но тогда при каждой перерисовке сумма пересчитывается заново все три раза, даже если ничего не менялось. На большом списке это заметные лишние вычисления. Функция computed решает это: результат кешируется и пересчитывается только когда меняется один из его реактивных источников. Это рабочая лошадка любого Vue-компонента.

  • Итоговая сумма заказа: производное от списка товаров, количества и скидки, пересчитывается только при их изменении
  • Отфильтрованный и отсортированный список из массива и условий фильтра без дублирования логики в шаблоне
  • Полное имя из имени и фамилии, форматированная дата, количество выбранных элементов - типовые computed
  • Флаг валидности формы, выведенный из значений всех полей, для блокировки кнопки отправки
  • Двунаправленная привязка к нормализованному значению через записываемый computed с get и set

Предварительные знания

  • Понимание ref и reactive из урока vue-06
  • Знакомство с системой отслеживания зависимостей из урока vue-05
  • JavaScript: чистые функции, методы массивов filter, map, reduce
  • Опыт работы с интерполяцией в шаблоне

computed: значение из состояния

Функция computed принимает функцию-геттер и возвращает реактивное значение, выведенное из других реактивных источников. Когда меняется любой источник, который геттер читает, computed пересчитывается. Это убирает дублирование: логику вывода значения пишут один раз, а не повторяют в каждом месте шаблона.

В примере total выводится из items. Vue отслеживает, что геттер читает items.value, и связывает computed с этим источником. Добавление товара меняет items, и total пересчитывается сам. В шаблоне computed используется как обычное значение - total - без вызова и без .value, потому что верхнеуровневый ref разворачивается автоматически.

computed возвращает ref только для чтения. В скрипте к нему обращаются через total.value, как к обычному ref, но присвоить ему значение напрямую нельзя - это вызовет предупреждение. Запись возможна только у специального записываемого computed, который разбирается в третьем концепте.

Что делает функция computed?

Кеширование: computed против метода

Тот же результат можно получить методом - обычной функцией, вызываемой в шаблоне. Поведение на экране будет одинаковым, но механика разная. computed кеширует результат: он хранит вычисленное значение и отдаёт его без пересчёта, пока не изменится реактивная зависимость. Метод выполняется заново при каждом вызове, потому что Vue не знает, изменится ли его результат, и вызывает его на всякий случай при каждой перерисовке.

  • computed (с кешем) — Пересчитывается только когда меняется реактивный источник. Многократное чтение в шаблоне возвращает один и тот же кешированный результат без повторных вычислений.
  • Метод (без кеша) — Выполняется при каждом вызове в шаблоне и при каждой перерисовке компонента. Для тяжёлых вычислений это заметная лишняя нагрузка.

В примере метод totalMethod вызывается дважды и при каждой перерисовке выполняется заново оба раза. computed totalComputed вычисляется один раз, а оба обращения в шаблоне получают кешированный результат. На маленьком списке разница незаметна, но на тяжёлой фильтрации или сортировке тысяч элементов кеширование экономит реальное время.

Правило выбора простое: если значение выводится только из реактивного состояния и не зависит от аргументов, берут computed ради кеша. Метод оправдан, когда нужно передавать параметры или когда результат должен пересчитываться при каждом вызове намеренно.

В чём главное практическое преимущество computed перед методом, вызываемым в шаблоне?

Записываемый computed через get и set

По умолчанию computed только читается. Но иногда нужно не только выводить значение, но и реагировать на попытку его записать - например, при двунаправленной привязке к нормализованному виду. Для этого в computed передают не функцию-геттер, а объект с двумя методами: get вычисляет значение, set обрабатывает присваивание и обычно обновляет исходные источники.

В примере fullName читается через get, склеивая имя и фамилию. При вводе в поле через v-model срабатывает set: он разбивает строку и обновляет firstName и lastName. Так computed становится посредником между удобным для пользователя видом (одно поле) и нормализованным хранением (два отдельных значения). Чтение и запись остаются согласованными.

Геттер computed должен оставаться чистым: только вычислять и возвращать значение, без побочных эффектов вроде запросов к серверу, изменения других переменных или работы с DOM. Если внутри геттера менять состояние, поведение станет непредсказуемым. Для побочных эффектов существует watch из следующего урока.

Как создать computed, в который можно не только читать, но и записывать?

Связь с другими темами

Этот урок про производные значения. Дальше идут наблюдатели для побочных эффектов:

  • watch и watchEffect — Когда нужен не вывод значения, а побочный эффект на изменение, выбирают наблюдатель, а не computed
  • ref и reactive — computed читает реактивные источники, созданные через ref и reactive

Итог

  • computed создаёт значение, выводимое из реактивного состояния, и автоматически пересчитывается при изменении его источников
  • Ключевое отличие от метода - кеширование: computed возвращает запомненный результат и пересчитывается только когда меняется реактивная зависимость, а метод выполняется при каждом вызове
  • computed возвращает ref только для чтения, в шаблоне он разворачивается автоматически, а в скрипте читается через .value
  • По умолчанию computed только читается, но через объект с get и set создаётся записываемый computed для двунаправленной привязки
  • computed должен быть чистым: только вычислять и возвращать значение, без побочных эффектов вроде запросов или мутаций - для них есть watch

Связанные уроки

  • vue-08-watchers — computed и watch оба реагируют на изменения реактивного состояния, но computed возвращает значение, а watch выполняет побочный эффект
  • vue-06-ref-reactive — computed строится поверх ref и reactive из предыдущего урока как их производное
computed: производные значения

0

1

Войти