From the previous article
I wrote an article the other day, sortable.js -- Vue data update problem At that time, I only analyzed the data from the perspective of forced refresh, and I didn't find the real "culprit".
Thank someone for pointing out that the key value of Vue may lead to incorrect data rendering. Therefore, I made a further attempt.
An incorrect use of key -- using index as key
I wonder if you will directly use index as its key value when writing v-for. Yes, I admit I will. I have to say that this is really not a good habit.
According to the previous article, we still use sortable.js as an example. The following is the core code, where the value of arrData is [1,2,3,4]
<div id="sort"> <div v-for="(item,index) in arrData" :key="index" > <div>{{item}}</div> </div> </div> Copy 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 code
Of course, at the beginning, there must be no problem with data rendering
OK, let's look at the following operations:
You can see that when I drag 3 to 2, the following data becomes 1342, but the view above is 1234. Then when I drag the fourth position to the third position, the following data also takes effect, but the above data seems to be all disordered. Well, we recreated the crime scene.
Then I changed the key value of the binding. Because the example here is special, we think 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 code
Look at the effect again:
Yes, at this time, the data is completely synchronized with the view.
Why?
Let's first look at an introduction to key in the official document
Child elements with the same parent element must have a unique key. Duplicate keys can cause rendering errors.
The reason for the above rendering error is that our key value is not unique. For example, after adjusting the array order, the original key value of each item changes, resulting in rendering error.
Let's draw a conclusion first. Using index as the key value is hidden unless you can guarantee that index can always be used as a unique identifier
What's the use of key value
After vue 2.0, if we don't write the key, we will report warning. That is to say, the official wants us to write the key value. What role does the key play in vue?
Can performance be improved without using key? The answer is yes! sure!
First look at the official explanation:
Without keys, Vue uses an algorithm that minimizes dynamic elements and tries to repair / reuse elements of the same type as much as possible. Using key, it rearranges the order of elements based on the change of key, and removes elements that do not exist.
For example, if an array [1,2,3,4] becomes [2,1,3,4], a "local update strategy" will be adopted if there is no key value, as shown in the figure below. It does not move the position of the element node, but directly modifies the element itself, which saves some performance
For elements with key values, the update method is shown in the following figure. As you can see, it is a removal / addition operation for DOM, which is more performance-consuming.
The performance is better without a key. Why do you need to bring a key? Let's take a look at an example. The core code is as follows. Here we imitate the function of a switching tab, that is, the switching tab1 is 1,2,3,4. tab2 is 5,6,7,8. There is a function of clicking to set the first font color to red.
Then, when we click tab1 to set the font color to red, and then switch to tab2, we expect the result to be the initial 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 code
trunToTab1 () { this.arrData = [1,2,3,4] }, trunToTab2 () { this.arrData = [5,6,7,8] }, clickItem () { document.getElementsByClassName('item')[0].style.color = 'red' } Copy code
This is beyond our expectation, that is, the official document says that the default mode refers to the state without key. This mode is not applicable to those that depend on the sub component state or temporary DOM state.
This default mode is efficient, but only applies to list rendered output that does not depend on subcomponent state or temporary DOM state (for example, form input value).
Let's look at the effect with the key
This is why the official document recommends us to write a key. According to the introduction of the document, it is as follows:
Using key, it rearranges the order of elements based on the change of key, and removes elements that do not exist. It can also be used to force replacement of elements / components rather than reuse it. It may be useful when you encounter the following scenarios:
- Trigger the lifecycle hook of the component completely
- Trigger transition
So how does Vue's underlying key value achieve the above functions? We have to talk about diff algorithm and virtual DOM.
The role of key in diff algorithm
Here, we will not talk about the specific of diff algorithm, but only look at the role of key value in it. (we'll talk about diff algorithm when we have a chance)
Look at 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 code
Let's sort out the code blocks:
// If there is a key if (isUndef(oldKeyToIdx)) { // Create index table oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); } if (isDef(newStartVnode.key)) { // If you have a key, you can get it directly from the above creation idxInOld = oldKeyToIdx[newStartVnode.key] } else { // If there is no key, call findIdxInOld idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx); } Copy code
The most important thing is the comparison between the createKeyToOldIdx and findIdxInOld functions. What did 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 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 code
We can see that if we have a key value, we can directly find the corresponding value in the map object created in the createKeyToOldIdx method according to our key value. If there is no key value, you need to traverse to get it. Mapping is faster than traversal.
The key value is the unique identifier of each vnode. Relying on the key, we can get the corresponding node in oldVnode faster.
reference resources
diff algorithm for parsing vue2.0
Welcome to my front-end grocery store~
Author: Gopal
Link: https://juejin.cn/post/6844903865930743815
Source: rare earth Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.