Could someone explain what is the difference between those 2 v-for structures:
<li v-for="item in items" :key="item">
</li>
and
<li v-for="(item, i) in items" :key="i">
</li>
CodePudding user response:
Vue requires all items inside a v-for
to be "key-ed". The key is used to uniquely identify each element. This doesn't mean Vue will break if you don't use a key
. It will warn it might not be able to detect all changes.
The "key" is particularly useful and important to Vue, as it allows it to skip re-rendering items which have not changed.
When duplicate keys are detected across a rendered collection, Vue will issue a warning.
Another Vue recommendation is that the "key"s are primitives (string
s or number
s). When you specify non-primitive keys Vue will, again, issue a warning.
Given all the above recommendations, when rendering an array of unique primitives, using
<div v-for="item in items" :key="item" />
...is perfectly acceptable, as it meets the requirements: each key is primitive and unique.
Therefore, if you change the order of the items, Vue will be able to re-use the existing DOM elements and perform any move transitions (if you specified any). Testing this is actually fun. Consider this example, taken from the Vue documentation. Open it and, using dev-tools, change any of the rendered items' color
to red
. Then click the shuffle button. You'll see Vue re-uses the element and your custom change is kept, as the item is moved around.
When you're dealing with collections of non-primitives, or with collections of non-unique primitives, Vue still expects you to provide a unique primitive key for each item. Ideally, you should have a unique identifier (e.g: item.id
). This is the typical solution and meets all requirements.
Sometimes, you don't have a unique identifier on each item, and an easy solution is to just use the items' position in the array as identifier (key):
<div v-for="(item, index) in items" :key="index" />
However, remember this can become problematic, particularly in cases where you change the order of the items after they're rendered and expect Vue to react to this change. It won't! Because Vue only watches the keys. When you swap items, keys don't change. only values do, so Vue won't re-render.
More detailed explanations and some examples can be found in the docs:
- Vue2 list rendering: https://v2.vuejs.org/v2/guide/list.html
- Vue3 list rendering: https://vuejs.org/guide/essentials/list.html#list-rendering
To give you an example of how keys can be really useful and powerful in terms of rendering performance, have a look at this virtual scroller I wrote for another SO question.
While you're scrolling, I suggest you open the dev tools and look at the markup. You'll notice that rows and columns do not get updated unless they need to be (each div flashes swiftly when it gets changed, at least in Chrome).
If you look into the code, you'll notice the keys are dynamic, but based on those dynamic results, Vue is able to track and reuse DOM elements, allowing you to scroll 1 billion cells smoothly (in theory!; in practice - we're only rendering as many as we can fit on the screen 1 extra row 1 extra column).
CodePudding user response:
In the first case, the v-for iterates over all elements of items
. It assigns the :key
to the item itself. This is not ideal if you have duplicate elements in items
, because each :key
should be unique.
In the second case, v-for also iterates over all elements of items
, but it introduces another variable named i
, which represents the numerical index of the item
in items
. This index is then assigned to :key
. This is better, because the indexes can't be duplicate.