이 글에서는 Vuejs의 핵심 가치에 대한 관련 지식을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
몇일전에 sortable.js - Vue 데이터 업데이트 문제라는 글을 썼는데, 당시에는 데이터 강제 새로고침 관점에서만 분석을 했고, 진짜 문제를 찾지 못했습니다.
데이터가 잘못 렌더링되는 원인이 Vue
의 key
값일 수 있다는 점을 누군가가 지적해 주셔서 정말 감사합니다. 이를 토대로 추가적인 시도를 하게 되었습니다. [관련 권장 사항: "vue.js 튜토리얼Vue
的 key
值,导致数据渲染不正确的。由此,我做了进一步的尝试。【相关推荐:《vue.js教程》】
index
作为 key
不知道你在写 v-for
的时候,会不会直接使用 index
作为它的 key
值,是的,我承认我会,不得不说,这真的不是一个好习惯。
根据上篇文章,我们还是用 sortable.js
作为例子讨论。以下是核心代码,其中 arrData
的值为 [1,2,3,4]
<div id="sort"> <div v-for="(item,index) in arrData" :key="index" > <div>{{item}}</div> </div> </div>
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) } }) }
当然一开始的时候,数据渲染肯定是没有问题的
好了,我们来看下以下的操作:
可以看到,我将3拖到2上面的时候,下面的数据变成了 1342,但是上面视图的还是1234。然后我第四位置拖到第三位置的时候,下面的数据也是生效的,但是上面的数据似乎全部错乱了。很好,我们重现了案发现场。
接着我改了绑定的 key
值,因为这里的例子比较特殊,我们就认为 item
的值都不相同
<div id="sort"> <div v-for="(item,index) in arrData" :key="item" > <div>{{item}}</div> </div> </div>
再看效果:
是的,这个时候数据就完全跟视图同步了。
为什么?
先看官方文档中 key
的一句介绍
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
之所以会造成上面渲染错误的情况,是因为我们的 key
值不是独特的,比如上面的 key
值,在调整数组顺序后就每一项原来的 key
值都变了,所以导致了渲染错误。
我们先来得出一个结论,用 index
作为 key
值是有隐患的,除非你能保证 index
始终能够能够作为一个唯一的标识
在 vue2.0
之后,我们不写 key
的话,就会报 warning
,那也就是说官方是希望我们写 key
值的,那么 key
到底在 vue
中扮演了什么样的角色?
不使用 key 可以提高性能么答案是,是的!可以!
先看官方解释:
如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
比如现在有一个数组 [1,2,3,4]变成了[2,1,3,4],那么没有 key
的值会采取一种“就地更新策略”,见下图。它不会移动元素节点的位置,而是直接修改元素本身,这样就节省了一部分性能
而对于有 key
值的元素,它的更新方式如下图所示。可以看到,这里它对 DOM 是移除/添加的操作,这是比较耗性能的。
竟然不带 key
性能更优,为何还要带 key先来看一个例子,核心代码如下,这里模仿一个切换 tab
"]
index
를 key
로 사용🎜🎜v-for
를 작성할 때는 몰랐습니다. index
를 key
값으로 직접 사용하지 않을 것입니다. 예, 이는 실제로 좋은 습관이 아니라는 점을 인정합니다. 🎜🎜이전 기사를 바탕으로 우리는 여전히 sortable.js
를 예시로 사용하여 논의하고 있습니다. 다음은 arrData
의 값이 [1,2,3,4]🎜<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>
trunToTab1 () { this.arrData = [1,2,3,4] }, trunToTab2 () { this.arrData = [5,6,7,8] }, clickItem () { document.getElementsByClassName('item')[0].style.color = 'red' }🎜인 핵심 코드입니다. 물론 처음에는 데이터 렌더링에는 문제가 없을 것입니다🎜🎜🎜🎜좋아, 해보자 다음 작업을 살펴보세요. 🎜🎜🎜 🎜 3을 2 위에 드래그하면 아래 데이터는 1342가 되지만 위 뷰는 여전히 1234인 것을 볼 수 있습니다. 그러다가 네 번째 위치를 세 번째 위치로 드래그하면 아래 데이터도 적용되는데 위 데이터가 다 엉망인 것 같았습니다. 좋습니다. 범죄 현장을 재현했습니다. 🎜🎜그런 다음 바인딩된
key
값을 변경했습니다. 여기 예제는 매우 특별하기 때문에 item
의 값이 다르다고 생각합니다🎜if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)🎜효과를 보세요. 다시:🎜 🎜
key
소개를 읽어보세요🎜🎜동일한 상위 요소를 가진 하위 요소에는 고유한 키가 있어야 합니다. 키가 중복되면 렌더링 오류가 발생합니다. 🎜🎜위 렌더링 오류가 발생하는 이유는 우리의
key
값이 고유하지 않기 때문입니다. 예를 들어 위의 key
값이 배열 순서로 조정되었습니다. 이후 원래 key
값이 각각 변경되어 렌더링 오류가 발생했습니다. 🎜🎜먼저 결론을 내리자: index
가 항상 가능하다는 것을 보장할 수 없다면 index
를 key
값으로 사용하는 것은 위험합니다. 고유 식별자로 사용됩니다🎜vue2.0
이후, 키
를 쓰지 않으면 경고
가 보고됩니다. 이는 공무원이 키
값을 쓰라고 한 다음 >keyvue
에서 /code>는 어떤 역할을 합니까? 🎜🎜키를 사용하지 않고도 성능을 향상시킬 수 있습니까?답은 '그렇다'입니다! 할 수 있다! 🎜🎜공식 설명을 먼저 보세요: 🎜🎜키를 사용하지 않는 경우 Vue는 동적 요소를 최소화하고 동일한 유형의 요소를 최대한 수리/재사용하는 알고리즘을 사용합니다. 키를 사용하면 키 변경에 따라 요소의 순서를 재정렬하고 키가 존재하지 않는 요소를 제거합니다. 🎜🎜예를 들어 배열 [1,2,3,4]가 [2,1,3,4]가 되면
key
가 없는 값은 " 내부 업데이트 전략'을 참조하세요. 아래 그림을 참조하세요. 요소 노드의 위치를 이동하지 않고 요소 자체를 직접 수정하여 일부 성능을 절약합니다🎜🎜key
값이 있는 요소의 경우 업데이트 방법은 아래 그림과 같습니다. 보시다시피, 여기서는 상대적으로 성능이 많이 소모되는 DOM에 대한 제거/추가 작업입니다. 🎜🎜🎜🎜key
가 없으면 성능이 더 좋습니다. 왜 key를 가져와야 할까요 먼저 예제를 살펴보겠습니다. 여기서는 전환 기능을 모방합니다. >tab
즉, 전환된 tab1은 1,2,3,4입니다. tab2는 5,6,7,8입니다. 첫 번째 항목을 클릭하면 글꼴 색상이 빨간색으로 설정되는 기능이 있습니다. 🎜那么当我们点击tab1将字体色设置成红色之后,再切换到 tab2,我们预期的结果是我们第一项字体的初始颜色而不是红色,但是结果却还是红色。
<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>
trunToTab1 () { this.arrData = [1,2,3,4] }, trunToTab2 () { this.arrData = [5,6,7,8] }, clickItem () { document.getElementsByClassName('item')[0].style.color = 'red' }
这就超出了我们的预期了,也就是官方文档所说的,默认模式指的就是不带 key
的状态,对于依赖于子组件状态或者临时 DOM 状态的,这种模式是不适用的。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
我们来看带上 key
之后的效果
这就是官方文档之所以推荐我们写 key
的原因,根据文档的介绍,如下:
使用
key
,它会基于key
的变化重新排列元素顺序,并且会移除key
不存在的元素。 它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景的时候它可能会很有用:
- 完整地触发组件的生命周期钩子
- 触发过渡
那么 Vue
底层 key
值到底是怎么去做到以上的功能?我们就得聊聊 diff
算法以及虚拟 DOM
了。
这里我们不谈 diff
算法的具体,只看 key
值在其中的作用。(diff
算法有机会我们再聊)
看 vue
源码中 src/core/vdom/patch.js
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
我们整理一下代码块:
// 如果有带 key if (isUndef(oldKeyToIdx)) { // 创建 index 表 oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); } if (isDef(newStartVnode.key)) { // 有 key ,直接从上面创建中获取 idxInOld = oldKeyToIdx[newStartVnode.key] } else { // 没有key, 调用 findIdxInOld idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx); }
那么最主要还是 createKeyToOldIdx
和 findIdxInOld
两个函数的比较,那么他们做了什么呢?
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 }
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 } }
我们可以看到,如果我们有 key
值,我们就可以直接在 createKeyToOldIdx
方法中创建的 map
对象中根据我们的 key
值,直接找到相应的值。没有 key
值,则需要遍历才能拿到。相比于遍历,映射的速度会更快。
key
值是每一个 vnode
的唯一标识,依靠 key
,我们可以更快的拿到 oldVnode
中相对应的节点。
更多编程相关知识,请访问:编程视频!!
위 내용은 Vuejs의 핵심 가치에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!