v-for中key的作用與原理

一、虛擬DOM中key的作用

key是虛擬DOM對象的標識,當數據發生變化時,Vue會根據新數據生成新的虛擬DOM,隨後Vue會對新虛擬DOM與舊虛擬DOM的差異進行比較。

二、如何選擇key

最好使用每條數據的唯一標識作為key,用一個簡單的例子說明:

1.用index作為key時

點擊按鈕在列表最前方添加趙六用戶

  <ul>
      <li v-for="(p,index) of persons" :key="index">
        {{p.name}}
        <input type="text">
      </li>
      <br>
      <button @click="Add">在最前方添加一個用戶</button>
  </ul>
 data () {
    return {
      persons: [
        {id: '01', name: '張三'},
        {id: '02', name: '李四'},
        {id: '03', name: '王五'}
      ]
    }
  },
  methods: {
    Add () {
      let p = {id: '04', name: '趙六'}
      this.persons.unshift(p)
    }
  }

點擊以後,頁面如下圖
image

然而,如果在我點擊按鈕前,在輸入框中輸入一些內容
image

點擊按鈕後就會變成
image
很明顯,這並不是我們理想中想要呈現的效果。

2.用id作為key時

<ul>
   <li v-for="p of persons" :key="p.id">
      {{p.name}}
      <input type="text">
   </li>
   <br>
   <button @click="Add">在最前方添加一個用戶</button>
</ul>

在輸入框中輸入與之前相同的內容,點擊按鈕後這次的頁面則變成了下圖
image
很明顯,這次就對了!

三、原因分析

為什麼用id和index作為key出來的效果天差地別呢,首先來分析一下key的作用原理。

用index作為key時:

image

首先會根據初始數據生成虛擬DOM,然後將虛擬DOM轉為真實DOM,在加入新數據以後,再根據新數據生成新的虛擬DOM,此時Vue並不會再重新將新虛擬DOM直接轉為真實DOM,而是進行一個虛擬DOM的對比算法。如下:

image

首先在新的虛擬DOM中按照順序取出第一項,然後根據標識「key=0」在舊的虛擬DOM中尋找擁有一樣「key=0」的節點,然後開始挨個對比。

image

第一個節點為文本節點,Vue發現一個為張三一個為趙六,不一致,於是將新的文本節點趙六轉為真實DOM

image

然後來到第二個標籤節點input,需要注意的是,我們對文本框的輸入是在真實DOM中操作的,但在虛擬DOM中兩邊的標籤節點input是一樣的,所以這時候Vue不會將它重新轉成真實DOM,而是直接復用左邊的。

image

以此類推,key等於1和等於2時,文本節點重新轉為真實DOM,標籤節點復用。

image

當來到key等於3這一項時,發現左邊並沒有key是等於3的一項,所以這個時候,Vue直接將右邊key等於3這一項直接轉為真實DOM。

用id作為key時:

image

首先也是按照順序取出第一項,然後根據標識「key=04」在左邊尋找擁有同樣key的一項,發現沒有,於是直接轉為真實DOM。

image

然後按順序取出第二項,根據標識「key=01」,在左邊尋找也是「key=01」的一項,挨個對比文本節點和標籤節點,發現兩個節點都完全一樣,所以直接復用之前的真實DOM。

image

以此類推,李四和王五都是直接復用左邊的真實DOM。

四、總結

  1. 用index作為key可能會引發的問題:若對數據進行逆序添加、逆序刪除等破壞順序的操作時,會產生沒有必要的真實DOM更新,此時界面效果沒有問題,但效率太低。
  2. 在開發中如何選擇key:最好使用每條數據的唯一標識作為key,比如id、身份證號、手機號等,如果不存在對數據的逆序添加、逆序刪除等破壞順序操作,僅用於渲染列表用於展示,使用index作為key是沒有問題的。