首頁 >web前端 >Vue.js >深入淺析Vuejs中的key值

深入淺析Vuejs中的key值

青灯夜游
青灯夜游轉載
2021-06-02 14:14:492653瀏覽

這篇文章跟大家介紹一下Vuejs中key值的相關知識。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

深入淺析Vuejs中的key值

從前篇文章說起

前幾天我寫了一篇文章,sortable.js——Vue 資料更新問題 ,當時自己只是資料的強制刷新角度去分析,而且並沒找到真正的「元兇」。

很感謝有人幫我指出,可能是 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中的key值

好了,我們來看以下的動作:

深入淺析Vuejs中的key值

可以看到,我把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 值,在調整陣列順序後就每一項原來的 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值

#而對於有key 值的元素,它的更新方式如下圖所示。可以看到,這裡它對 DOM 是移除/新增的操作,這是比較耗性能的。

深入淺析Vuejs中的key值

竟然不帶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值

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

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

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

深入淺析Vuejs中的key值

这就是官方文档之所以推荐我们写 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中的key值的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除