Vue中key的作用及原理詳解
1. 先說結論
- key在Vue是DOM對象的標識;
- 進行列表展示時,默認key是index;
- 如果數據隻做展示使用,使用index作為key是沒有任何問題的;
- 如果使用index作為key,而後續操作會破壞順序,一定會帶來效率問題,嚴重時會渲染出錯誤的DOM
關於key的作用及實現原理,下面一一道來。
2. key的作用
key就是一個標識,被使用在Vue中。再詳細一點,key被使用在Vue中的虛擬DOM中,並不會出現在真實DOM中。
2.1 舉一個例子
以列表的形式展示一組人員信息。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>key的原理</title> <!--引入vue--> <script type="text/javascript" src="../js/vue.js"></script> </head> <div id="root"> <h2>人員列表</h2> <ul> <li v-for="(p,index) in persons"> {{p.name}}-{{p.age}} </li> </ul> </div> <body> <script type="text/javascript"> const vm = new Vue({ el:'#root', data:{ persons:[ {'id':'001', 'name':'張三','age':'18'}, {'id':'002', 'name':'李四','age':'19'}, {'id':'003', 'name':'王五','age':'20'} ] } }) </script> </body> </html>
這個html文件在瀏覽器中打開如下圖所示。
而上述示例html文件中並沒有使用到key,似乎也沒有問題。當然,單純地展示數據,不寫key是不會存在問題的。
現在我們為上述示例加上key,這裡以每條數據的id為key
<li v-for="(p,index) in persons" :key="p.id"> {{p.name}}-{{p.age}} </li>
加上key的展示結果和上圖結果一模一樣。
而如果我們在瀏覽器上查看元素,不會看到key的存在。
截至目前,我們可以得到兩個結論:1. 隻做數據展示用,不寫key是沒有任何影響的;2.key不會出現在真實DOM中
實際上,即使不寫key,Vue在生成真實DOM時,也用到瞭key,默認是數據索引(index)
我們把key替換為index,展示的數據不會產生任何改變。
<li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}} </li>
2.2 修改一下上述示例
在展示人員信息的基礎上顯示索引,並且添加一個按鈕,功能是在頭部添加人員信息
對上述html文件稍加修改。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>key的原理</title> <!--引入vue--> <script type="text/javascript" src="../js/vue.js"></script> <link rel="icon" href="../favicon.ico" type="image/x-icon" /> </head> <div id="root"> <h2>人員列表</h2> <button @click="add">添加一個老劉</button> <ul> <li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}}-{{index}} </li> </ul> </div> <body> <script type="text/javascript"> const vm = new Vue({ el:'#root', data:{ persons:[ {'id':'001', 'name':'張三','age':'18'}, {'id':'002', 'name':'李四','age':'19'}, {'id':'003', 'name':'王五','age':'20'} ] }, methods:{ add(){ const p = {'id':'004', 'name':'老劉','age':'40'} this.persons.unshift(p) } } }) </script> </body> </html>
我們可以看到,張三、李四、王五的索引分為別0,1,2
點擊按鈕,添加一個新人物,這個時候索引發生瞭變化,新添加的人物“老劉”變為瞭索引0,似乎對,也似乎不對
當然,單純地討論索引,這裡一點問題也沒有,下面舉一個新例子,來說說“不對“在哪裡
2.3 再修改一下示例
不展示索引瞭,改為輸入框,在每個人物後面的輸入框內寫上人物的姓,觀察新插入數據後原始數據的變化
稍微修改一下html
<li v-for="(p,index) in persons" :key="index"> {{p.name}}-{{p.age}} <input type="text"> </li>
實際效果就是下圖這樣
到這裡,似乎沒有什麼不對,接下來就是見證奇跡的時刻
添加老劉,出現瞭問題,和我們預想的不一樣。
這是key為index的情況,如果修改為數據的唯一標識,則不會產生這樣的問題。
<li v-for="(p,index) in persons" :key="p.id"> {{p.name}}-{{p.age}} <input type="text"> </li>
誒,這就是我們想要的。
列表內有輸入內容,後續操作會破壞原始順序,就會產生錯誤DOM
3. key的實現原理
要解釋key的實現原理,就要引入Vue一個十分重要的概念——【虛擬DOM】。
給出一組數據,Vue要把這些數據渲染到頁面上,首先要生成【虛擬DOM】,然後根據【虛擬DOM】去生成【真實的DOM】。如果數據發生瞭改變,Vue會生成【新的虛擬DOM】,註意,這個【新的虛擬DOM】並不會直接生成【新的真實DOM】,否則虛擬DOM一點用處也沒有瞭。Vue的操作是,拿根據新的數據生成的【新的虛擬DOM】與之前的【真實的DOM】去做比較,如果相同,直接延用即可(“拿來主義”);如果不同,則生成新的DOM對象。
在這個過程中key扮演瞭很重要的角色。
根據最後一個示例進行剖析。
1. key為index的情況。
根據數據生成【真實DOM】的流程如下:(註意,下圖的真實DOM中輸入框裡的內容為生成頁面後手動添加)
然後,添加人物“老劉”,獲取到一組新數據
Vue拿新數據生成【新的虛擬DOM】
在生成真實DOM,就需要用新生成的虛擬DOM和原來的真實DOM作比較(一條一條分析)
對比第一條,key為0,找到舊DOM中key為0的數據,發現“老劉-40”和“張三-18”不同,渲染新的數據“老劉-40”到頁面上;再往後,發現同為輸入框,不必重新渲染,直接使用原來真實DOM的內容。第一條內容就出現瞭,而這個輸入框還攜帶有張三的姓。
對比第二條,key為1,找到舊DOM中key為1的數據,發現“張三-18”和“李四-19”不同,渲染新的數據“張三-18”到頁面上;再往後,發現同為輸入框,不必重新渲染,直接使用原來真實DOM的內容。第二條內容就出現瞭,而這個輸入框還攜帶有李四的姓。
之後同理。
回顧這個過程,key是作為虛擬DOM中對象的唯一標識,標識出瞭數據的“身份信息”,Vue在虛擬DOM中會根據這個“身份標識”去對比內容,設計的初衷是為瞭節省資源開支,不必渲染重復的部分。在本示例中,不但帶來瞭效率問題,還渲染出瞭錯誤的DOM,後果非常嚴重。
2. key為id的情況。
直接進入添加“老劉”後的新舊DOM對比。
對比第一條,key為‘004’,發現在舊DOM中並不存在,直接生成“老劉-40”和新的輸入框。
對比第二條,key為‘001’,發現舊DOM中key為‘001’的數據相同,直接將“張三-18”和輸入框拿過來使用。
……
最後生成正確的DOM,節省瞭資源開支。
總結
推薦使用數據的唯一標識作為key,比如id,身份證號,手機號等等,通常這些數據由後端提供。
後續操作不破壞原來數據順序的話,使用index作為key也沒有任何問題。
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!