Keyed elements are discarded
Version
3.0.0-rc.10
Reproduction link
https://codesandbox.io/s/keyed-elements-are-discarded-demo-jfuoz?file=/src/App.vue
Steps to reproduce
Create two elements of equal type.
Give the elements different keys.
At runtime, switch the keys of the elements around, so that the first element in the template now has the key of the second one, and vice versa.
What is expected?
The elements should be reused.
What is actually happening?
The elements are discarded and new ones are created in their place.
In the repro, I've set it up to keep a reference to the initial elements, and compare them to the current divs being used.
If the elements were reused, the computed values should toggle from true to false on each click of the button, but they're only true initially, and then they stay false forever.
The same can also be observed in these demos set up by Ranseur of the Vue Land discord.
Vue 2: https://jsfiddle.net/0pnujhbx/1/
Vue 3: https://jsfiddle.net/vs8Lyc3o/1/
The elements are reused in Vue 2, but discarded in Vue 3.
Just added here for the record: the optimization mode doesn't have a mechanism to reuse DOM, and the patchBlockChildren
is just a one-to-one diff, we cannot guarantee that the VNodes in dynamicChildren are at the same level, so there is no way to achieve key-based reuse.
I thought of a solution: due to a Fragment with KEYED_FRAGMENT
flag will always patch its children, if there are Element nodes with the dynamic key in children, then we need to wrap the children
with a fragment with KEYED_FRAGMENT
flag.
Unfortunately this will be the expected behavior in Vue 3 due to how templates are now compiled differently to enable better update performance. In brief, template node structures are always treated as "stable" and key
can now only force replacement of the a node itself, but can no longer enforce reuse of nodes that are in different static locations in the template.
This is a borderline breaking change, since using key
to enforce reuse of non v-for
nodes isn't technically a documented behavior, and while the behavior is different, the end rendering output isn't "wrong".