>웹 프론트엔드 >View.js >Vuejs의 핵심 가치에 대한 심층 분석

Vuejs의 핵심 가치에 대한 심층 분석

青灯夜游
青灯夜游앞으로
2021-06-02 14:14:492651검색

이 글에서는 Vuejs의 핵심 가치에 대한 관련 지식을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

Vuejs의 핵심 가치에 대한 심층 분석

이전 글에 이어

몇일전에 sortable.js - Vue 데이터 업데이트 문제라는 글을 썼는데, 당시에는 데이터 강제 새로고침 관점에서만 분석을 했고, 진짜 문제를 찾지 못했습니다.

데이터가 잘못 렌더링되는 원인이 Vuekey 값일 수 있다는 점을 누군가가 지적해 주셔서 정말 감사합니다. 이를 토대로 추가적인 시도를 하게 되었습니다. [관련 권장 사항: "vue.js 튜토리얼Vuekey 值,导致数据渲染不正确的。由此,我做了进一步的尝试。【相关推荐:《vue.js教程》】

key 的一个错误使用——使用 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(&#39;sort&#39;)
    var sortable = new Sortable(el, {
      onEnd: (e) => {
        const tempItem = this.arrData.splice(e.oldIndex, 1)[0]
        this.arrData.splice(e.newIndex, 0, tempItem)
      }
    })
  }

当然一开始的时候,数据渲染肯定是没有问题的

Vuejs의 핵심 가치에 대한 심층 분석

好了,我们来看下以下的操作:

Vuejs의 핵심 가치에 대한 심층 분석

可以看到,我将3拖到2上面的时候,下面的数据变成了 1342,但是上面视图的还是1234。然后我第四位置拖到第三位置的时候,下面的数据也是生效的,但是上面的数据似乎全部错乱了。很好,我们重现了案发现场。

接着我改了绑定的 key 值,因为这里的例子比较特殊,我们就认为 item 的值都不相同

<div id="sort">
  <div v-for="(item,index) in arrData" :key="item" >
    <div>{{item}}</div>
  </div>
</div>

再看效果:

Vuejs의 핵심 가치에 대한 심층 분석

是的,这个时候数据就完全跟视图同步了。

为什么?

先看官方文档中 key 的一句介绍

有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

之所以会造成上面渲染错误的情况,是因为我们的 key 值不是独特的,比如上面的 key 值,在调整数组顺序后就每一项原来的  key 值都变了,所以导致了渲染错误。

我们先来得出一个结论,index 作为 key 值是有隐患的,除非你能保证 index 始终能够能够作为一个唯一的标识

key 值到底有什么用

vue2.0 之后,我们不写 key 的话,就会报 warning,那也就是说官方是希望我们写 key 值的,那么 key 到底在 vue 中扮演了什么样的角色?

不使用 key 可以提高性能么答案是,是的!可以!

先看官方解释:

如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

比如现在有一个数组 [1,2,3,4]变成了[2,1,3,4],那么没有 key 的值会采取一种“就地更新策略”,见下图。它不会移动元素节点的位置,而是直接修改元素本身,这样就节省了一部分性能

Vuejs의 핵심 가치에 대한 심층 분석

而对于有 key 值的元素,它的更新方式如下图所示。可以看到,这里它对 DOM 是移除/添加的操作,这是比较耗性能的。

Vuejs의 핵심 가치에 대한 심층 분석

竟然不带 key 性能更优,为何还要带 key先来看一个例子,核心代码如下,这里模仿一个切换 tab"]

키의 잘못된 사용 - indexkey로 사용🎜🎜v-for를 작성할 때는 몰랐습니다. indexkey 값으로 직접 사용하지 않을 것입니다. 예, 이는 실제로 좋은 습관이 아니라는 점을 인정합니다. 🎜🎜이전 기사를 바탕으로 우리는 여전히 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(&#39;item&#39;)[0].style.color = &#39;red&#39;
      }
🎜인 핵심 코드입니다. 물론 처음에는 데이터 렌더링에는 문제가 없을 것입니다🎜🎜🎜🎜좋아, 해보자 다음 작업을 살펴보세요. 🎜🎜2. gif🎜
🎜 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가 항상 가능하다는 것을 보장할 수 없다면 indexkey 값으로 사용하는 것은 위험합니다. 고유 식별자로 사용됩니다🎜

키 값의 용도는 무엇인가요🎜🎜vue2.0 이후, 를 쓰지 않으면 경고가 보고됩니다. 이는 공무원이 값을 쓰라고 한 다음 >keyvue에서 /code>는 어떤 역할을 합니까? 🎜🎜키를 사용하지 않고도 성능을 향상시킬 수 있습니까?답은 '그렇다'입니다! 할 수 있다! 🎜🎜공식 설명을 먼저 보세요: 🎜
🎜키를 사용하지 않는 경우 Vue는 동적 요소를 최소화하고 동일한 유형의 요소를 최대한 수리/재사용하는 알고리즘을 사용합니다. 키를 사용하면 키 변경에 따라 요소의 순서를 재정렬하고 키가 존재하지 않는 요소를 제거합니다. 🎜
🎜예를 들어 배열 [1,2,3,4]가 [2,1,3,4]가 되면 key가 없는 값은 " 내부 업데이트 전략'을 참조하세요. 아래 그림을 참조하세요. 요소 노드의 위치를 ​​이동하지 않고 요소 자체를 직접 수정하여 일부 성능을 절약합니다🎜🎜🎜🎜 key 값이 있는 요소의 경우 업데이트 방법은 아래 그림과 같습니다. 보시다시피, 여기서는 상대적으로 성능이 많이 소모되는 DOM에 대한 제거/추가 작업입니다. 🎜🎜Vuejs의 핵심 가치에 대한 심층 분석🎜🎜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(&#39;item&#39;)[0].style.color = &#39;red&#39;
      }

Vuejs의 핵심 가치에 대한 심층 분석

这就超出了我们的预期了,也就是官方文档所说的,默认模式指的就是不带 key 的状态,对于依赖于子组件状态或者临时 DOM 状态的,这种模式是不适用的。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。

我们来看带上 key 之后的效果

Vuejs의 핵심 가치에 대한 심층 분석

这就是官方文档之所以推荐我们写 key 的原因,根据文档的介绍,如下:

使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。 它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景的时候它可能会很有用:

  • 完整地触发组件的生命周期钩子
  • 触发过渡

那么 Vue 底层 key 值到底是怎么去做到以上的功能?我们就得聊聊 diff 算法以及虚拟 DOM 了。

key 在 diff 算法中的作用

这里我们不谈 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);
  }

那么最主要还是 createKeyToOldIdxfindIdxInOld 两个函数的比较,那么他们做了什么呢?

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제