Кэшируются ли вычисляемые свойства во Vue и как работает мемоизация
Что проверяют
Понимаете ли вы, как устроен кэш computed: от чего он зависит, когда сбрасывается и почему это не то же самое, что метод. Следом почти всегда спрашивают, чем computed отличается от метода в шаблоне и от watch, а иногда просят объяснить, почему «застыло» свойство с Date.now() внутри.
Как отвечать
Да, кэшируются. Механика такая:
- зависимости собираются при вычислении: пока выполняется геттер, Vue запоминает все реактивные источники, которые он прочитал:
ref, поляreactive, пропсы, другиеcomputed; - сброс ленивый: при изменении зависимости значение только помечается устаревшим. Пересчёт случится при следующем обращении, а если к свойству никто не обращается, геттер не вызовется вовсе;
- метод вызывается на каждый рендер:
{{ total() }}пересчитается при любой перерисовке компонента, а{{ total }}изcomputedвернёт сохранённое значение; - в Vue 3 результат это ref: в
<script setup>его читают через.value, в шаблоне он разворачивается сам. В свежих версияхcomputedк тому же не будит зависимые эффекты, если новое значение совпало со старым.
Мемоизация в общем смысле это кэш результата по входным данным. computed её частный случай: ключом служит состояние реактивных зависимостей, аргументов у него нет. Если нужна мемоизация функции с параметрами, её пишут сами через Map или берут готовую. Для шаблона есть ещё v-memo, который пропускает перерисовку поддерева, пока не изменился массив зависимостей, и v-once для разового рендера.
Где спотыкаются
Кладут в геттер нереактивный источник: Date.now(), localStorage, поле обычного объекта. Vue не знает, что оно поменялось, и computed так и отдаёт первое значение.
Делают в computed побочные эффекты или мутации. Классика: list.value.sort() сортирует исходный массив на месте. Правильно [...list.value].sort() или toSorted(), а запросы и запись в стор уходят в watch.
Возвращают из computed функцию, чтобы передать аргумент. Кэшируется тогда сама функция, а её вызов в шаблоне считается заново на каждом рендере, и выигрыша нет.