- Вычисляемые свойства и слежение
- Вычисляемые свойства
- Простой пример
- Кэширование вычисляемых свойств
- Вычисляемые свойства и слежение
- Сеттеры вычисляемых свойств
- Методы-наблюдатели
- Vue.js computed property not working
- 7 Answers 7
- Vue 3: когда свойство computed может быть неправильным инструментом
- Что особенного в computed
- Кеширование
- Lazy Evaluation
- Когда lazy evaluation может улучшить производительность
- Когда lazy evaluation может снизить производительность
- Демонстрация проблемы
- Как решить эту проблему, когда вы с ней сталкиваетесь.
- Простые функции
- Getters
- Пользовательский eagerComputed helper
- Заключение
Вычисляемые свойства и слежение
Вычисляемые свойства
Встраиваемые в шаблоны выражения удобны, но могут предназначаться только для простых операций. При усложнении логики их труднее поддерживать:
Такой шаблон уже не выглядит простым и декларативным. С первого взгляда и не скажешь, что он всего лишь отображает message задом наперёд. Ситуация станет ещё хуже, если эту логику потребуется использовать в нескольких местах шаблона.
На помощь здесь приходят вычисляемые свойства.
Простой пример
Мы определили вычисляемое свойство reversedMessage . Написанная нами функция будет использоваться как геттер свойства vm.reversedMessage :
Вы можете открыть консоль и поиграть с примером самостоятельно. Значение vm.reversedMessage всегда зависит от значения vm.message .
В шаблонах можно обращаться к вычисляемым свойствам как и к обычным. Vue знает, что vm.reversedMessage зависит от vm.message , поэтому при обновлении vm.message обновятся и все зависящие от него элементы, в нашем случае обновится vm.reversedMessage . Самое важное — эту зависимость теперь мы указали декларативно: геттер вычисляемого свойства не имеет побочных эффектов, что упрощает понимание кода и его тестирование.
Кэширование вычисляемых свойств
Можно заметить, что такого же результата можно достичь и с помощью метода:
Вместо вычисляемого свойства, можно использовать ту же самую функцию в качестве метода. С точки зрения конечного результата, оба подхода делают одно и то же. Но есть важное отличие: вычисляемые свойства кэшируются, основываясь на своих реактивных зависимостях. Вычисляемое свойство пересчитывается лишь тогда, когда изменится одна из его реактивных зависимостей. Поэтому, пока message остаётся неизменным, многократное обращение к reversedMessage будет каждый раз возвращать единожды вычисленное значение, не запуская функцию вновь.
Обратите внимание, что следующее вычисляемое свойство никогда не обновится, поскольку Date.now() не является реактивной зависимостью:
Использование метода, напротив, будет запускать функцию всегда, при каждом обращении к нему.
Зачем нужно кэширование? Представьте, есть «дорогое» вычисляемое свойство A, требующее цикла по огромному массиву и выполняющее множество вычислений. И пусть ещё будут другие вычисляемые свойства, в свою очередь, зависящие от A. Без кэширования геттер A будет запускаться куда чаще необходимого! В тех же случаях, когда кэширования нужно избежать — используйте методы.
Вычисляемые свойства и слежение
Vue предоставляет и более общий способ наблюдения и реагирования на изменения данных в экземпляре: слежение за свойствами. Когда есть данные, которые необходимо обновлять при изменении других данных, возникает соблазн избыточно использовать только этот подход, особенно если привыкли к Angular. Но, как правило, лучше использовать вычисляемые свойства, а не императивный коллбэк в watch . Рассмотрим пример:
Код выше — императивный и избыточный. Сравните с версией с использованием вычисляемого свойства:
Так гораздо лучше, не правда ли?
Сеттеры вычисляемых свойств
По умолчанию вычисляемые свойства работают только на чтение, но при необходимости можно также указать и сеттер:
Теперь запись vm.fullName = ‘Иван Иванов’ вызовет сеттер, и vm.firstName и vm.lastName будут соответствующим образом обновлены.
Методы-наблюдатели
Хотя в большинстве случаев лучше использовать вычисляемые свойства, иногда необходимы пользовательские методы-наблюдатели. Поэтому Vue предоставляет более общий способ реагирования на изменения в данных через опцию watch . Эта возможность полезна для «дорогих» или асинхронных операций, выполняемых в ответ на изменение данных.
Задайте вопрос, на который можно ответить «да» или «нет»:
В данном случае использование опции watch позволило выполнять асинхронную операцию (обращение к API), ограничивать частоту выполнения этой операции и устанавливать промежуточные состояния до получения ответа от сервера. Ничего из этого не удалось бы достичь с помощью вычисляемых свойств.
Кроме опции watch можно также использовать vm.$watch в императивном стиле.
Источник
Vue.js computed property not working
I have a users data object which has a first_name and last_name and a computed property which returns the full name but the following doesnt seems to work in my v-for directive?
7 Answers 7
I don’t believe that a computed property in Vue.js can accept an argument, they are supposed to be able to build themselves without additional interaction. I believe that your fault was that you wrote a method and then tried to register it as a computed property.
So it should be a simple fix to just change the registration of your function from computed to methods which it really is.
This way you will not have to change any of your other code.
Try to disable cache on the computed property, like mentioned in https://github.com/vuejs/vue/issues/1189. Doing this, the computed value will be always recalculated.
Computed properties don’t support nesting like that.
Proof and some workarounds/alternative solutions: https://github.com/vuejs/vue/issues/66
You can map the users in your computed property and use that mapped array like this:
Then in view you can do something like:
And I am using es6 arrow function. If you don’t want to use arrow function then you can do something like:
i had a similar case, when i use a props property with undecleared nested properties. For some reason i made this work doing a
Also, i made this work using lambda expression https://stackoverflow.com/a/46234335/726751
Another reason: properties with an underscore ( _ ) prefix will not trigger computed .
Surprise: _ and $ prefixes are reserved in Vue.
But, in the end here is the possible solution for you from the conclusion that i get after discussing and trial and error. There are several ways:
- create extender that assign the computed inside each element, then call it using item.computedProperty() in the UI (with double bracket)
- assume the data is loaded, for example items , create a watcher that will assign the computed to each of the element, then put inside extendedItems . the computed function will become reactive when bound to UI, so the UI then will use extendedItems instead of items
- use vue’s this.$set to manually set and enable reactivity to the computed.
This is using computed with function from the solution number 1:
Источник
Vue 3: когда свойство computed может быть неправильным инструментом
Если вы используете Vue в свой работе, то вы вероятно, знаете свойства computed (вычисляемое свойство), а если вы похожи на меня, вы, вероятно, думаете, что это очень полезный инструмент!
Для меня computed — это очень эргономичный и элегантный способ работы с производным состоянием, то есть состоянием, которое состоит из другого состояния (его зависимостей). Но в некоторых сценариях это свойство может ухудшить производительность приложения, и как мне кажется, многие люди не знают об этом, поэтому эта статья посвящена объяснению этой проблемы.
Чтобы прояснить, что мы имеем ввиду, когда мы говорим computed во Vue, рассмотрим этот небольшой пример:
Здесь openTodos является производным от todos, а hasOpenTodos — производным от openTodos. В этом примере у нас есть реактивные объекты, которые мы можем передавать и использовать, и они будут автоматически обновляться при изменении состояния, от которого они зависят.
Если мы используем эти реактивные объекты в реактивном контексте, таком как шаблон Vue, функция рендеринга или watch(), также будут реагировать на изменения нашего вычисляемого свойства и обновлять его результат.
Примечание: в этой статье я использую Composition API , просто потому что оно мне больше нравиться на данный момент. Однако поведение, описанное в этой статье, применимо и к вычисляемым свойствам в обычном Options API . В конце концов, оба используют одну и ту же систему реактивности.
Что особенного в computed
Есть два нюанса computed, которые делают их особенными и имеют отношение к сути этой статьи:
- Их результаты кэшируются, и их необходимо повторно вычислять только после изменения одной из его реактивных зависимостей.
- Они вычисляются отложенным способом (evaluated lazily), то есть только при доступе.
Кеширование
Результат computed кэшируется. В нашем примере выше это означает, что до тех пор, пока массив задач todos не изменяется, многократный вызов openTodos.value вернет одно и то же значение без повторного запуска метода фильтрации. Это особенно удобно для дорогостоящих задач (задач потребляющих много ресурсов), так как это гарантирует, что задача будет запускаться повторно только тогда, когда это необходимо, а именно, когда одна из ее реактивных зависимостей изменилась.
Lazy Evaluation
Computed свойства также evaluated lazily — но что именно это означает?
Это означает, что функция обратного вызова свойства computed (callback) будет запускаться только после того, как вычисленное значение будет прочитано (изначально или после того, как оно было помечено для обновления, потому что одна из его зависимостей изменилась).
Поэтому, если свойство computed с дорогостоящими вычислениями ни где не используется, то дорогостоящая операция вообще не будет выполняться — еще одно улучшение производительности при выполнении тяжелой работы с большим количеством данных.
Когда lazy evaluation может улучшить производительность
Как объяснялось в предыдущем абзаце, lazy evaluation свойств computed очень полезно для дорогостоящих операций: оно гарантирует, что вычисление выполняется только тогда, когда результат действительно необходим. А это означает, что фильтрация большого списка, будет просто пропущена, если этот отфильтрованный результат не будет прочитан и использован какой-либо частью вашего кода в этот момент. Вот краткий пример:
Проверить этот код можно тут SFC Playground
Поскольку showList изначально имеет значение false, функция рендеринга не будет читать openTodos, и, следовательно, фильтрация даже не будет запускаться ни изначально, ни после добавления нового todo и изменения todos.length. Только после того, как для showList будет установлено в значение true, эти вычисленные свойства будут прочитаны, и это вызовет их вычисление.
Конечно, в этом небольшом примере объем работы по фильтрации минимален, но вы можете себе представить, что для более дорогих операций это может быть огромным преимуществом.
Когда lazy evaluation может снизить производительность
У этого есть обратная сторона: если результат, возвращаемый computed, будет известен только после того, как ваш код где-то его использует, то система реактивности Vue не узнает его возвращаемое значение заранее.
Другими словами, Vue может выяснить, что одно или несколько зависимостей свойства computed изменилось, поэтому его следует повторно вычислить при следующем чтении, но Vue не может в этот момент понять, был ли результат, возвращенный свойством computed другим.
Почему это может быть проблемой?
Другие части вашего кода могут зависеть от свойства computed — это может быть другое свойство computed, или это может быть функция watch(), или какая другая функция.
Таким образом, у Vue нет другого выбора, кроме как пометить это свойство для обновления — «на всякий случай» вдруг возвращаемое значение будет другим.
Если это дорогостоящие операции, вы могли бы вызвать дорогостоящее вычисление, даже если ваше свойство computed будет возвращать то же значение, что и раньше, и поэтому новое вычисление было ненужным.
Демонстрация проблемы
Вот краткий пример: представьте, что у нас есть список элементов и кнопка для увеличения счетчика. Как только счетчик достигнет 100, мы хотим показать список в обратном порядке.
(Вы можете поиграть с этим примером тут SFC playground)
Вопрос: Вы нажимаете кнопку в 101 раз. Будет ли в этот момент наш компонент перерисовывается?
Ответ: Да, будет выполнен 101 повторный рендеринг
Я подозреваю, что некоторые из вас могли ожидать другого ответа, например: «один раз, при 101-м щелчке». Но это неверный ответ, и причина этого — lazy evaluation свойств computed.
Давай те рассмотрим, что происходит, шаг за шагом:
- Когда мы нажимаем кнопку, увеличивается count. Компонент не будет повторно визуализироваться, потому что мы не используем счетчик count в шаблоне.
- Но поскольку count изменился, наше свойство computedisOver100 помечено как «dirty» — реактивная зависимость изменилась, и поэтому его возвращаемое значение должно быть повторно вычислено.
- Но из-за lazy evaluation это произойдет только после того, как что-то еще прочитает isOver100.value — до того, как это произойдет, мы (и Vue) не знаем, вернет ли это свойство computedfalse или изменится на true.
- sortedList зависит от isOver100, поэтому оно также должен быть помечен как dirty. Точно так же оно еще не будет вычислено, потому что это происходит только тогда, когда оно читается.
- Поскольку наш шаблон зависит от sortedList, и он помечен как «dirty» (потенциально изменен, требует вычисления), компонент будет повторно рендерится.
- Во время рендеринга он считывает sortedList.value
- Теперь вычисляется sortedList и считывается isOver100.value — который теперь вычисляется, и при этом снова возвращает false.
- Итак, теперь мы повторно отрисовали компонент и повторно запустили «дорогое» свойство computedsorteList , хотя все это было ненужным — получившийся новый шаблон будет выглядеть точно так же.
Настоящим виновником является isOver100 — это computed свойство, которое часто обновляется, но возвращает одно и то же значение, что и раньше, и, кроме того, это дешевая операция, которая на самом деле не приносит пользы от кэширования вычисляемых свойств. Мы просто использовали свойство computed, потому что оно показалось эргономичным и «красивым».
Как решить эту проблему, когда вы с ней сталкиваетесь.
К настоящему времени у вас может возникнуть два вопроса:
- Насколько это является проблемой?
- Как мне избегать эту проблему?
Итак, во-первых, остынь. Обычно это не большая проблема. Система реактивности Vue, как правило, очень эффективна, и повторные рендеры тоже, особенно во Vue 3. Обычно пара ненужных обновлений здесь и там по-прежнему будет работать намного лучше, чем ее аналог в React, который по умолчанию повторно рендерит все в любом состоянии.
Таким образом, проблема относится только к конкретным сценариям, где у вас есть сочетание частых обновлений состояния в одном месте, которые запускают частые ненужные обновления в другом месте.
Если вы столкнулись с такой ситуацией, к счастью, у вас есть разные способы ее решения:
- Использование простых функций вместо автономных свойств computed
- Использование Getters вместо свойств computed
- Использование настраиваемого свойства «eagerly computed»
Простые функции
Этот раздел о простых функциях нуждается в тщательном пересмотре, я объединил несколько вещей из предыдущего черновика этой статьи, что в итоге привело к неверному выводу.
Можете сразу перейдите к третьему пункту о «eager сomputed», чтобы прочитать о безошибочном варианте решения.
Если операция вычисляемого свойства является дешевым однострочным, мы можем вместо этого использовать функцию:
Оба способа предлагают описательное именование, но второй вариант, вероятно, немного лучше для общей производительности, так как простая функция меньше использует память и процессор, чем свойство computed, а ее операция — считывание длины массива — настолько простая, что использование вычисляемого кеша не принесет никакой выгоды по сравнению с этим.
А простая функция не будет иметь lazy evaluation , поэтому мы не рискуем получить ненужные эффекты функции рендеринга, или другого свойства computed .
В большинстве случаев это может не оказать большого влияния, но в определенных сценариях такое может произойти. Только представьте себе компонент, который использует несколько таких вычисляемых свойств и многократно отображается в большом списке — здесь использование функций вместо свойства computed может наверняка сэкономить вам немного памяти.
Я бы сказал, что использование свойства computed само по себе почти во всех случаях нормально. Если вы предпочитаете стиль свойства computed простой функции, во что бы то ни стало делайте то, что вам больше нравится.
Getters
Я также видел такой образец кода:
И он может быть удобным, если вы хотите иметь объект, в котором одни свойства наследуются от других.
Но на самом деле computed в этом примере было бы излишним. В Javascript есть собственный способ получения состояния для свойства объекта, называемый Getters . У него нет кеширования или lazy evaluation, в любом случае мы не особо выигрываем от использования такого сценарии.
Пользовательский eagerComputed helper
Функции и геттеры — это хорошо, но для тех из нас, кто привык к тому, как работает Vue, computed может показаться более приятным. К счастью, система реактивности Vue дает нам все необходимые инструменты для создания нашей собственной версии computed(), которая вычисляется eagerly (моментально), а не как lazily (отложено).
Назовем это eagerComputed()
Затем мы можем использовать это, как если бы мы использовали computed, но разница в поведении заключается в том, что обновление будет моментальным, а не отложенным, что избавить нам от ненужных обновлений.
Когда лучше использовать computed(), а когда eagerComputed()?
- Используйте computed(), когда у вас происходит сложное вычисление, которое может фактически использовать пользу из кеширования и отложенных вычислений и должно повторно вычисляться только в случае необходимости.
- Используйте eagerComputed(), когда у вас простая операция с редко меняющимся возвращаемым значением — часто с логическим значением.
Примечание: имейте в виду, что это все равно добавит немного накладных расходов, поскольку использует набор API-интерфейсов реактивности — простая функция обычно более эффективна в очень чувствительных сценариях.
Заключение
Мы углубились в то, как на самом деле работают свойства computed. Мы узнали, когда они полезны для производительности вашего приложения, а когда могут ухудшить ее. Что касается последнего сценария, мы рассмотрели 3 различных способа решения проблемы производительности, избегая ненужных реактивных обновлений.
Я надеюсь, что это статья была полезной для вас.
Источник