From the previous article
A few days ago, I wrote an article, sortable.js — Vue data update problem. At that time, I only analyzed data from the perspective of forced refresh, and did not find the real “culprit”.
Thank you for pointing out to me that it may be the key value of Vue that causes the data rendering to be incorrect. So I took it a step further.
An incorrect use of key — useindex
As akey
I don’t know if you would use index as the key when you write v-for, but yes, I do, and I have to say, that’s not a good habit.
Based on the previous article, we will use sortable.js as an example for discussion. Here is the core code, where arrData is [1,2,3,4]
<div id="sort">
<div v-for="(item,index) in arrData" :key="index" >
<div>{{item}}</div>
</div>
</div>
Copy the code
mounted () {
let el = document.getElementById('sort')
var sortable = new Sortable(el, {
onEnd: (e) => {
const tempItem = this.arrData.splice(e.oldIndex, 1)[0]
this.arrData.splice(e.newIndex, 0, tempItem)
}
})
}
Copy the code
Of course, in the beginning, data rendering is no problem
Ok, let’s look at the following operations:
As you can see, when I drag 3 over 2, the bottom number becomes 1342, but the top view is still 1234. Then when I drag the fourth position to the third position, the following data is also valid, but the above data seems to be all confused. Good. We reconstructed the crime scene.
And then I change the key of the binding, because this is a special case, and we assume that the value of item is different
<div id="sort">
<div v-for="(item,index) in arrData" :key="item" >
<div>{{item}}</div>
</div>
</div>
Copy the code
Look again at the effect:
Yes, at this point the data is completely synchronized with the view.
Why is that?
Let’s take a look at key in the official document
Child elements that have the same parent must have a unique key. Duplicate keys cause render errors.
The reason for the above rendering error is that our key value is not unique. For example, the above key value changed the original key value of each item after adjusting the array order, resulting in the rendering error.
Let’s start with the conclusion that using index as a key is dangerous unless you can guarantee that index will always be a unique identifier
What does a key value do anyway
After VUE 2.0, if we do not write a key, we will report a warning. That is, we are expected to write a key.
Can you improve performance without keys the answer is yes! You can!
First, the official explanation:
If keys are not used, Vue uses an algorithm that minimizes dynamic elements and tries to repair/reuse the same type of elements as much as possible. With a key, it rearranges elements based on the change in the key and removes elements where the key does not exist.
For example, if an array [1,2,3,4] becomes [2,1,3,4], values without keys will adopt an “in-place update strategy”, as shown in the figure below. Instead of moving the element node, it directly modifies the element itself, which saves some performance
For an element with a key value, it is updated as shown in the figure below. As you can see, here it is a remove/add operation to the DOM, which is very performance intensive.
Tab1 (1,2,3,4); tab1 (1,2,3,4); Tab2,6,7,8 five. There is a function to click and set the first font color to red.
So when we click on Tab1 to set the font color to red and switch to Tab2, we expect the original color of our first font instead of red, but the result is still red.
<div id="sort">
<button @click="trunToTab1">tab1</button>
<button @click="trunToTab2">tab2</button>
<div v-for="(item, index) in arrData">
<div @click="clickItem(index)" class="item">{{item}}</div>
</div>
</div>
Copy the code
trunToTab1() {this.arrdata = [1,2,3,4]},trunToTab2() {this.arrdata = [5,6,7,8]},clickItem () {
document.getElementsByClassName('item')[0].style.color = 'red'
}
Copy the code
This is unexpected, as the official documentation states that the default mode refers to keyless state and is not suitable for dependencies on child component state or temporary DOM state.
This default mode is efficient, but only for list rendering output that does not depend on child component state or temporary DOM state (for example, form input values).
So let’s see what happens when we add a key
This is the reason why the official document recommends us to write key. According to the introduction of the document, it is as follows:
With a key, it rearranges elements based on the change in the key and removes elements where the key does not exist. It can also be used to force elements/components to be replaced rather than reused. It can be useful when you encounter situations like:
- Triggers the component’s lifecycle hook completely
- Trigger a transition
So how does Vue bottom key value achieve the above function? We need to talk about diff algorithms and the virtual DOM.
The role of key in diff algorithm
We’re not going to talk about the diff algorithm, we’re just going to look at the key. (Diff algorithm, we’ll talk about it later.)
See SRC /core/vdom/patch.js in vue source code
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
Copy the code
Let’s tidy up the code block:
// If there is a key
if (isUndef(oldKeyToIdx)) {
// Create index table
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
}
if (isDef(newStartVnode.key)) {
// There is a key, obtained directly from the above creation
idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
// No key, call findIdxInOld
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
}
Copy the code
So the main thing is to compare createKeyToOldIdx and findIdxInOld, so what do they do?
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
Copy the code
function findIdxInOld (node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
}
Copy the code
We can see that if we have a key value, we can directly find the corresponding value based on our key in the map object created in the createKeyToOldIdx method. If there is no key value, you need to iterate to get it. The mapping is faster than traversal.
key
The value is each of themvnode
The unique identifier of the dependencykey
We can get it fasteroldVnode
The corresponding node in.
reference
React/Vue: Why do you write a key in a list component when writing React/Vue?
Analyze the diff algorithm of vue2.0
Welcome everyone to pay attention to my front end big grocery store ~