- Правильный способ заставить Vue перендерить компонент
- Худший вариант: перезагрузить все страницу
- Плохой вариант: использовать v-if хак
- Способ получше: можно использовать forceUpdate
- Лучший способ: изменение ключа
- Зачем нужен атрибут key в Vue?
- Смена ключа для принудительного повторного рендеринга компонента
- Дополнение от переводчика
- forceUpdate does not update computed fields #7395
- Comments
- nagyzsolthun commented Jan 6, 2018
- Version
- Reproduction link
- Steps to reproduce
- What is expected?
- What is actually happening?
- JounQin commented Jan 6, 2018 •
- yyx990803 commented Jan 6, 2018
- anlek commented Sep 26, 2018
- ypresto commented Dec 4, 2018 •
- mannok commented Jan 30, 2019
- Serval Ways to Force Update in Vue #47
- Comments
- xianshenglu commented Nov 29, 2018
- xianshenglu commented Dec 1, 2018
- Auto Update with Vue’s Reactivity System
- $forceUpdate
- Reassign
- v-if/key
- No forceUpdate() in Vue 3 with compositionAPI #3562
- Comments
- mannok commented Apr 8, 2021
- Version
- Reproduction link
- Steps to reproduce
- What is expected?
- What is actually happening?
- HcySunYang commented Apr 8, 2021 •
- mannok commented Apr 8, 2021
- HcySunYang commented Apr 8, 2021
- mannok commented Apr 8, 2021
- HcySunYang commented Apr 8, 2021
- mannok commented Apr 8, 2021
- HcySunYang commented Apr 8, 2021
- mannok commented Apr 8, 2021
- mannok commented Apr 8, 2021
- posva commented Apr 8, 2021
- LinusBorg commented Apr 8, 2021 •
- HcySunYang commented Apr 8, 2021
- mannok commented Apr 8, 2021
- posva commented Apr 8, 2021 •
- HcySunYang commented Apr 9, 2021
- LinusBorg commented Apr 9, 2021
- posva commented Apr 9, 2021
- yyx990803 commented Apr 10, 2021 •
- mannok commented Apr 10, 2021
Правильный способ заставить Vue перендерить компонент
Эта публикация является любительским переводом статьи «The correct way to force Vue to re-render a component» от Майкла Тиссена.
Иногда бывает так, что реактивности Vue не хватает, и вам нужно перерендерить компонент. Или вам нужно сбросить DOM и начать сначала.
Так как же правильным образом заставить Vue перерендерить компонент?
Ответ прост. Правильный путь — использовать :key в компоненте. Когда вам нужно перерендерить компонент, вы просто меняете значение ключа и Vue обновит компонент.
Довольно просто, не так ли? Есть также и другие способы:
- Худший вариант: обновить всю страницу,
- Плохой вариант: использовать v-if хак,
- Чуть лучше: использовать встроенный в Vue метод forceUpdate ,
- Лучший вариант: использовать изменение ключа.
Обратите внимание! Если вы столкнулись с ситуацией, в которой вам нужно перерендерить компонент, вероятно, есть более правильный способ решить задачу. Скорее всего, вы не до конца разобрались с чем-то из этого: 1. Реактивность Vue, 2. Computed-свойства, 3. Watch-свойства, 4. Не используете :key атрибут для v-for .
А теперь рассмотрим доступные способы перерендерить компонент. Почти все из них заменяемы на вариант с изменением ключа, он рассмотрен в самом конце статьи.
Худший вариант: перезагрузить все страницу
Этот способ — примерно то же самое, что перезагрузка компьютера каждый раз, когда вы хотите закрыть какое-нибудь приложение. Звучит странно, правда?
Полагаю, это и правда может когда-то вам помочь. Однако это ужасное решение, поэтому не используйте его. Совсем.
Лучше посмотрите, что вы можете использовать ещё.
Плохой вариант: использовать v-if хак
Vue содержит директиву v-if, которая позволяет рендерить компонент или часть компонента только тогда, когда условие возвращает истину. Если в условии ложь, то всё что внутри не отрендерится и не будет существовать в DOM.
Давайте посмотрим, как это выглядит на практике.
Добавьте v-if в ваш шаблон:
В секции script добавьте метод, который использует функцию nextTick :
Вот что происходит в этом коде:
- Сначала renderComponent установлено в true, поэтому компонент my-component рендерится
- Когда мы вызываем forceRerender , мы сразу устанавливаем renderComponent в false
- Мы прекращаем рендеринг my-component , потому что v-if директива содержит ложь
- На следующем шаге ( nextTick ) мы возвращаем renderComponent в true
- Теперь v-if содержит истину, поэтому мы снова рендерим компонент my-component
В этом примере есть два важных момента, которые важны для понимания того, как это работает. Первое — мы должны дождаться nextTick, иначе мы не увидим никаких изменений.
В Vue tick (тик) — это один цикл обновления DOM. Vue собирает все обновления в одном тике, и в конце этого тика обновляет то, что отображается в DOM в процессе этих обновлений. Если мы не дождёмся следующего тика, то наши изменения свойства renderComponent перезапишут сами себя и ничего не изменится. Поэтому мы используем nextTick .
Второе, Vue создаст совершенно новый компонент, когда мы отрендерим его второй раз. Vue уничтожит первый компонент и создаст новый. Это значит, что наш новый my-component пройдёт все хуки жизненного цикла компонента — created , mounted
Кстати, вы можете использовать nextTick с промисами:
Тем не менее, это не очень хорошее решение. Скорее, это хак. Поэтому давайте посмотрим, что ещё мы можем использовать.
Способ получше: можно использовать forceUpdate
Это один из двух способов, который отлично решает проблему. При этом, он официально поддерживается Vue и не является хаком или чем-то подобным.
Обычно Vue реагирует на изменения в зависимостях, обновляя представление. Однако, когда вы запускаете forceUpdate, вы можете принудительно запустить обновление представления, даже если зависимости никак не изменились.
И вот здесь большинство людей делает множество серьёзных ошибок.
Если Vue автоматически обновляет всё когда что-то меняется, то зачем нам самим заставлять его что-то обновлять?
Причина в том, что иногда реактивность Vue может сбивать с толку. Мы думаем, что Vue отреагирует на изменения свойства или переменной, но этого не происходит. Есть также определённые ситуации, когда реактивность Vue вообще не распознаёт ваши изменения.
Стоит отметить, что forceUpdate всё же не лучший способ решения задачи. Используйте его аккуратно.
There are two different ways that you can call forceUpdate , on the component instance itself as well as globally:
Важно: этот способ не обновляет computed-свойства. Вызов forceUpdate только заставляет шаблон перерендерится.
Лучший способ: изменение ключа
Что за ключ? Сейчас во всё разберёмся. Вообще, существует много ситуаций, когда перерендеринг может действительно вам потребоваться.
Для того, чтобы сделать это надлежащим образом, мы свяжем наш компонент с атрибутом key . Так Vue свяжет компонент с отдельным куском данных. И если ключ при изменениях останется прежним, то Vue не обновит компонент, а вот если ключ поменяется — Vue избавится от старого компонент и создаст новый. То, что нам нужно!
Давайте сперва пробежимся по тому, что такое key и почему нам нужно его использовать.
Зачем нужен атрибут key в Vue?
Давайте представим, что вы рендерите список компонентов, у которых есть одно или несколько из этих свойств:
- У компонента есть локальный стейт,
- У компонента есть логика инициализации: что-то, что использует mounted или created хуки,
- Не реактивные DOM-манипуляции: обычно это jQuery-решения или стандартные JS API.
Если вы отсортируете этот список или обновите его каким-либо образом, вам нужно будет повторно отрендерить все части этого списка. Вряд вы захотите перерендерить вообще все элементы этого списка, лучше затронуть лишь то, что изменилось.
Для помощи Vue в отслеживании таких изменений мы внедряем атрибут key. Использование индекса массива тут не поможет, поскольку индекс не привязан к конкретным объектам в нашем списке.
Here is an example list that we have:
Если мы отрендерим список с использованием индексов, то получим это:
Если мы удалим Марину, то получим:
Индекс, который был связан с Николаем изменился, хотя Николай всё ещё Николай. При этом, Николай перерендерится, хотя мы этого не хотели.
В нашей ситуации подойдёт какой-то уникальный ID, давайте добавим его.
Когда мы удаляли Марину из нашего списка, Vue удалил компоненты для Марины и Николая, а затем создал новый компонент для Николая. Теперь, когда Vue знает ID, он сохранит два компонента Вани и Николая, и удалит лишь компонент Марины.
Если мы добавим ещё кого-то в список, то Vue знает, что может сохранить текущие компоненты и просто создать один дополнительный. Это действительно полезно, когда у вас есть более сложные компоненты с логикой, собственным состоянием, имеют логику инициализации или выполняют какие-то манипуляции с DOM.
А теперь давайте продолжим с лучшим методом для принудительного повторного рендеринга компонентов в Vue.
Смена ключа для принудительного повторного рендеринга компонента
Наконец-то, лучший способ принудительного повторного рендеринга компонента (по мнению автора, конечно же).
Вы применяете эту стратегию назначения ключей дочерним элементам, но каждый раз, когда вы хотите повторно отрендерить компонент, вы просто обновляете ключ.
Вот простой пример того, как это работает:
Каждый раз, когда мы в компоненте вызываем forceRerender , наш пропс componentKey изменяется. Это в свою очередь приводит к тому, что Vue узнаёт когда нужно уничтожить инстанс компонента и создать новый.
В результате вы получаете дочерний компонент, который способен сам себя переинициализировать и сбросить своё состояние. Простой и элегантный способ решения проблемы.
И помните, что если вы столкнулись с ситуацией, когда вам нужно принудительно перерендерить компонент, то вероятно вы делаете что-то не лучшим образом. Если всё же вам нужно как-то перерендерить компонент, то выбирайте способ с привязкой ключа.
Дополнение от переводчика
Стоит упомянуть отдельно Vuex и связанную с ним реактивность. Дело в том, что если у вас в глобальном сторе хранится сложный объект или многомерный массив, то Vue не сможет отслеживать изменения в них, если делать это неправильно. В документации Vue, в описании реактивности, есть информация о том, как работать со сложными объектами и массивами, чтобы достичь реактивности.
Источник
forceUpdate does not update computed fields #7395
Comments
nagyzsolthun commented Jan 6, 2018
Version
Reproduction link
Steps to reproduce
- build project
- open build/build.html
- wait 2 seconds
What is expected?
«last update time» to change
What is actually happening?
«last update time» does not change
The text was updated successfully, but these errors were encountered:
JounQin commented Jan 6, 2018 •
As I know, forceUpdate is only used to force update UI, not for inter computed data.
yyx990803 commented Jan 6, 2018
As @JounQin pointed out: $forceUpdate only force the view to re-render, not the computed properties. Your computed property should not contain an external non-reactive variable (time), which makes it impure.
anlek commented Sep 26, 2018
@yyx990803 Any reference to how to make a updatable time work in the UI?
ypresto commented Dec 4, 2018 •
@anlek methods: are called on $forceUpdate . Move time function from computed to methods (don’t forget to change time to time() ) and it should work as expected.
mannok commented Jan 30, 2019
@yyx990803 You are right. It won’t recompute the properties. However, I encounter another issue today.
$forceUpdate is not updating reactive variable too!
for example:
template part: <
data in script part: data() < return < attr: 'foo' >>
If I update attr and the content in console by vm.$refs[‘lbl’].setAttribute(‘data-attr’, ‘bbb’) and vm.$refs[‘lbl’].innerText = ‘ccc’
follow by vm.$forceUpdate() , only the innerText part updated.
Источник
Serval Ways to Force Update in Vue #47
Comments
xianshenglu commented Nov 29, 2018
when you want to totally re-render the component, use key instead of $forceUpdate. Sometimes, it can solve the problem which $forceUpdate can’t solve.
The text was updated successfully, but these errors were encountered:
xianshenglu commented Dec 1, 2018
I have to mention this before we go on. As the doc-Forcing-an-Update says:
If you find yourself needing to force an update in Vue, in 99.99% of cases, you’ve made a mistake somewhere.
When I come back to write a blog, I find it’s totally true. Well, let’s begin. Here is the demo code based on element-ui@2.4.11 and vue@2.5.17:
Auto Update with Vue’s Reactivity System
Go check the doc.
$forceUpdate
For example, if someone made a mistake and wrote the code like
You will not see the data update because you just add an nonreactive attribute. In this case, you might need app.$forceUpdate() . However, better idea is using code like:
Reassign
However, it’s common to use third party components. In this case, it may not be easy to modify the component if we find something not good. For example, if we sort the data using:
Here is the result:
The part above the hr works as expected while the el-tree doesn’t change even you use $forceUpdate . So, in this case, what can we do?
We can make it update by reassigning like:
Until now, every thing about force update has been solved. I haven’t met problems which can’t be solved by ways above. However, there is some way stronger I have to mention.
v-if/key
In some cases, we do need to destroy and rebuild the component. But I didn’t find any API about rebuild except this issue. Some guys said we can rebuild using v-if=»isElTreeShow» :
Источник
No forceUpdate() in Vue 3 with compositionAPI #3562
Comments
mannok commented Apr 8, 2021
Version
Reproduction link
Steps to reproduce
In Vue2 we can call this.$forceUpdate() to force update a component. Once I switched to Vue 3 Composition API, seems this feature is lost. How can I get it back?
What is expected?
I can call $forceUpdate()
What is actually happening?
I cannot call $forceUpdate()
The text was updated successfully, but these errors were encountered:
HcySunYang commented Apr 8, 2021 •
This is indeed a lack of the Composition API, but you can easily use the existing API to make workarounds:
Maybe we need an implementation of the Composition API version of the corresponding capability, e.g.
mannok commented Apr 8, 2021
This is indeed a lack of the Composition API, but you can easily use the existing API to make workarounds:
Maybe we need an implementation of the Composition API version of the corresponding capability, e.g.
Thanks @HcySunYang, is i stand for instance?
HcySunYang commented Apr 8, 2021
Oh. sorry. yes, that should be instance
mannok commented Apr 8, 2021
Oh. sorry. yes, that should be instance
by the way, I don’t find a function named queueJob , is it the same with nextTick ?
HcySunYang commented Apr 8, 2021
Should be queuePostFlushCb :
I don’t recommend this, but it seems to be the only workaround at the moment
mannok commented Apr 8, 2021
@HcySunYang May I know what is the difference between queuePostFlushCb and nextTick ? Also, why should I put the update function inside queuePostFlushCb ? Can I call instance.update() instead? Thanks in advance.
HcySunYang commented Apr 8, 2021
The queueJob will put the update task in the async queue, and can de-duplicate the update task
mannok commented Apr 8, 2021
mannok commented Apr 8, 2021
I found that queueJob is not exposed for us. and queuePostFlushCb does not have the ability to de-duplicate the callback. May I know if I can use queueJob here?
posva commented Apr 8, 2021
$forceUpdate() is still there:
LinusBorg commented Apr 8, 2021 •
FWIW, I think it’s still worthwhile to provide a bette way to force an update than to go through the internal component instance.
We do want people to rely on this as little as possible, which is also why we don’t document anything about it’s APIs — it’s considered internal and more of an escape hatch for lib authors.
HcySunYang commented Apr 8, 2021
Yeah, I agree with @LinusBorg
mannok commented Apr 8, 2021
@LinusBorg Yes, I was stuck until I find this internal instance approach. Wish $forceUpdate can be exposed to developers.
posva commented Apr 8, 2021 •
We do want people to rely on this as little as possible, which is also why we don’t document anything about it’s APIs — it’s considered internal and more of an escape hatch for lib authors.
Yeah, what would we expose though?
Since it relies on the current instance, I don’t think we can directly expose forceUpdate()
Given the nature of this API, I’m not sure if it should be documented instead
HcySunYang commented Apr 9, 2021
LinusBorg commented Apr 9, 2021
we could alternatively add it to context?
posva commented Apr 9, 2021
I think we should avoid exposing a low level utility like forceUpdate() in such a visible place like the context. I personally think we should document this rather than add useForceUpdate() because it shouldn’t be used most of the time and people should search for this method and inform themselves of what it is intended for rather than just see the name with autocompletion and try to use it
yyx990803 commented Apr 10, 2021 •
I’ve actually never for once needed forceUpdate when using Vue 3 myself. I would rather know what use case this is absolutely needed for.
mannok commented Apr 10, 2021
@yyx990803 I used to develop geographical map with (ArcGIS JSAPI) and using Vue as my web framework. ArcGIS JSAPI has its own reactive system and seems to be implemented by getter/setter (like Vue2). However, I should avoid Vue3 to make it as reactive so that I always markRaw to those instances which belongs to this API. The problem is that Vue reactive system won’t know when the instance’s property is being mutated. Or I should say I know better than Vue in this case. Therefore, I know when should I call $forceUpdate() in order to update the template. (If part of the template depends on the instance’s property.)
Источник