Subscribe on changes!

`updated` is called but component wasn't updated (different from Vue 2)

avatar
Oct 3rd 2020

Version

3.0.0

Reproduction link

https://jsfiddle.net/7revor/xw0tLp65/42/

Steps to reproduce

1.Open the console.

2.Click the update button.

3.Observe the logging.

What is expected?

Child component would not have updated.

What is actually happening?

Child component updated.

avatar
Oct 3rd 2020

This is expected, it did update. It wouldn't trigger with nested components though

avatar
Oct 3rd 2020

This is expected, it did update. It wouldn't trigger with nested components though

This is expected, it did update. It wouldn't trigger with nested components though

This is actually a breaking change from v2.

In the process of migration, the resulting performance loss makes me very confused.

We have a huge amount of data, and each of them needs to be controlled whether it is displayed or not.

In v3, it takes an extra 150 milliseconds per second on computing useless rendering, and our FPS decreased from 60 to 50.

I suggest this should be documented.

avatar
Oct 3rd 2020

It's true the behavior is different from Vue 2 and I'm not even sure it makes sense for it to call the updated hook

avatar
Oct 3rd 2020

It's true the behavior is different from Vue 2 and I'm not even sure it makes sense for it to call the updated hook

In v2, it seems that component would not update even the v-show changed (update display:none only). But in v3, no matter the v-show changes or not, as long as the parent component updated, the child component will render again(there are no props changes).

avatar
Oct 3rd 2020

as long as the parent component updated, the child component will render again

That sounds like v2 behaviour if you used $attrs in the child.

avatar
Oct 3rd 2020

If I'm reading it right, shouldUpdateComponent will return true if there are any directives present on the VNode:

https://github.com/vuejs/vue-next/blob/1abcb2cf61ec16807cae11cfe56acefab19487a1/packages/runtime-core/src/componentRenderUtils.ts#L306

So any 'custom' directive would cause the same problem.

avatar
Oct 7th 2020

We can definitely optimize this but I wouldn't consider it a bug.

avatar
Jan 9th 2021

It took me a long time to figure out that this is what was killing performance in my app.

I am rendering ~3000 custom components (each rendering one SVG element) via v-for and had a v-show directive on the same elements that rendered via v-for. Clearly having that v-show leads the check (which @skirtles-code quoted) to succeed, so there are vdom update checks happening for all of those 3000 child components even when no data changes that are relevant to them happen.

I then followed this hint,

It wouldn't trigger with nested components though

basically just by extracting the <my-child v-for="element in myList" v-show="element.show" /> to an intermediate container component, <my-children :list="myList" />, whose template basically just repeats that <my-child v-for..., but as a consequence isolates it from the containing parent (where every irrelevant data changes were triggering updates). It now renders the exact same DOM elements, and only differs in the structure of the Vue component organization.

With just that, performance went up from super janky (~20fps) to almost 60fps for me!

I think enhancing this would benefit users with a similar use case like mine greatly, i.e., users who want to render large amounts of (list) data with mostly localized changes. I almost would have gone to migrate my Vue3 app to Vue2 (which I do not know) or would have dropped Vue altogether, if I hadn't found the info in this GitHub issue. Or maybe a temporary note in the docs that mentions/discusses this quirk could be added to this doc section https://v3.vuejs.org/guide/component-edge-cases.html#controlling-updates ?

Thanks for the hints everyone!