關於v-for的一點小總結

linliqzh發表於2018-12-29

簡單介紹

在vue裡面,我們通過v-for指令來渲染一個陣列列表,列表項需要我們定義一個別名,即:

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
複製程式碼

在上面這個例子中,items是我們要渲染的陣列列表,item是陣列列表項的別名,當然,我們可以給v-for新增第二個引數作為當前項的索引:

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ index }} - {{ item.message }}
  </li>
</ul>
複製程式碼

有經驗的同學可能會指出,上面的例子都少了一個屬性: key,是的,官方文件建議我們給v-for渲染的每一個列表項指定一個key,那麼,key的作用是什麼呢?vue官方文件給了以下的解釋:

當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它預設用“就地複用”策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的 track-by="$index" 。

這個預設的模式是高效的,但是隻適用於不依賴子元件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。

為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key 屬性。理想的 key 值是每項都有的唯一 id。這個特殊的屬性相當於 Vue 1.x 的 track-by ,但它的工作方式類似於一個屬性,所以你需要用 v-bind 來繫結動態值 (在這裡使用簡寫):

<div v-for="item in items" :key="item.id">
  <!-- 內容 -->
</div>
複製程式碼

建議儘可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴預設行為以獲取效能上的提升。因為它是 Vue 識別節點的一個通用機制,key 並不與 v-for 特別關聯,key 還具有其他用途,我們將在後面的指南中看到其他用途。

提出問題

問題1: 什麼是'就地複用'?

這裡我舉一個簡單的例子來說明一下:

在一個玩具組裝車間,小A, 小B, 小C,小D以及其他96個流水線工人一起完成組裝玩具的工作,每個人都有自己的一輛玩具在組裝,突然,小B肚子痛跑去上廁所了,但是小B的工作還沒完成,為了不影響整體進度,工人都依次往前補,小C頂替了小B的工作,小D頂替了小C的工作,以此類推,這樣雖然也是有點麻煩,還好工作按期完成了。

這就是就地複用大概的思路,如果不採用就地複用,那麼,當小B缺席之後,流水線上的工人(包括小B之前的)都要重新排序,100個人按照工號重新排序,重新獲取自己的工作內容,這樣子工作效率就大大降低了。

在DOM中,就地複用的是那些沒有變化的元素,例如:

<div v-for="item in items">
  <input/>
  {{item.message}}
</div>
複製程式碼

在這段程式碼中,沒有變化的元素就是input,當我們刪除items的某個元素時,item.message會發生變化,因此渲染的資料發生了變化,但是已經存在dom中的input卻會被就地複用,具體的演示效果如下:

關於v-for的一點小總結

問題2: 如何解決就地複用產生的問題?

正如我們上面看到的那樣,我們希望input也被重新渲染,這個問題的解決方案vue已經在文件中說的很清楚了,我們需要給每一個列表項加上一個唯一的key屬性

問題3: key屬性是如何解決問題的,它的原理是什麼?

這個問題就涉及到了虛擬DOM的diff演算法了,具體原理可以檢視知乎的一篇文章

問題4: 沒有新增key屬性或者key屬性不是唯一的會出現什麼坑?

key屬性需要繫結一個唯一的值,但是在我們的實際專案中,不一定會有這麼一個唯一的值讓我們繫結,因此習慣性我們會採用index作為key屬性的值,這樣做就容易產生問題了...

因為index對應的value值是會變化的,例如:

list: [
  {index: 0, value: 0},
  {index: 1, value: 1},
  {index: 2, value: 2}
]
複製程式碼

當我們執行list.splice(1, 1)移除index = 1的項時,list陣列就會變成:

list: [
  {index: 0, value: 0},
  {index: 1, value: 2}
]
複製程式碼

因為index是陣列元素的索引,當某個元素被移除時,被移除元素後面的元素索引都會更新,也就是說index並不是唯一的,所以vue就會採用 就地複用 原則,這時如果頁面上有類似於input這種跟value值沒有繫結關係的元素時,這些元素將會被複用,有可能就得不到我們要的效果了

總結: 相信用vue的人都會遇到這麼一個列表渲染元素混亂的問題,key屬性看似簡單但是第一次遇到的時候是會很納悶的,關於列表渲染比較常見的坑還有陣列和物件的監測和更新,this.$set你值得擁有,詳情可以檢視官方文件,本文如果有錯漏,歡迎指出~

相關文章