vue中key使用的問題示例解析
前言
在vue要求在遍歷的時候最好加上key,在使用過程中總有些疑問,在這里做下分析
不使用key的時候vue是怎么處理的
在vue2.x文檔中有如下描述
key 的特殊 attribute 主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對比時辨識 VNodes。如果不使用 key,Vue 會使用一種最大限度減少動態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法。而使用 key 時,它會基于 key 的變化重新排列元素順序,并且會移除 key 不存在的元素。
在這段話中提到不使用key的時候,會盡量原地復(fù)用,復(fù)用的判斷依據(jù)是常常在面試中被問到的sameVnode
這個方法:
function sameVnode (a, b) { return ( // 因?yàn)闆]有定義key,可以得知a.key和b.key都是undefined,進(jìn)而后面的條件進(jìn)行判斷; a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
使用key一定能提高diff效率么
答案是并不是,可以看下面的例子
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script> <div id="app"> <div class="ttt" v-for="(item) in list">{{item}}</div> <button @click="changeData">更新</button> </div> <script > new Vue({ el: '#app', data() { return { list: [1, 2, 3] } }, mounted(){ Array.from(document.querySelectorAll('.ttt')).forEach(item=>{ item.dataset.mm = item.textContent; }) }, methods:{ changeData(){ this.list = [4, 5, 6] } } }) </script>
初始dom結(jié)構(gòu)如下:
沒有key情況下更新數(shù)組,然后DOM結(jié)構(gòu)如下,可以看到dataset值沒有變化,可以初步判斷只是更新了div的內(nèi)容
使用數(shù)組元素作為key情況下更新數(shù)組,然后DOM結(jié)構(gòu)如下,可以看到dataset沒有了,可以初步判斷刪除和創(chuàng)建了新的dom,很明顯這樣處理效率更低。
vue源碼中的處理程序邏輯:
沒有key的情況: updateChildren
-> 判定為同類型節(jié)點(diǎn)(div),執(zhí)行patchVnode
方法 -> 再對div這個節(jié)點(diǎn)做updateChildren
處理 -> 繼續(xù)patchVnode
,最后的結(jié)果就是更新文本節(jié)點(diǎn)內(nèi)容;
if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) }
有key并且key不相等: updateChildren
-> key不同,判定不同類型節(jié)點(diǎn)(div),執(zhí)行createElm
方法,進(jìn)而創(chuàng)建新的dom節(jié)點(diǎn);
為什么說使用index作為key容易出錯
- 復(fù)現(xiàn)步驟:在第一行的Input里輸入1,在第二行Input里輸入2,然后點(diǎn)第一行的“ד刪除第一行
- 期待結(jié)果:刪除第一行后,應(yīng)該變成“dog:2”
- 實(shí)際結(jié)果:刪除第一行后,變成了“dog:1”
<script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script> <div id="app"> <ul> <li v-for="(item, index) in list" :key="index"> <test-row :name="item"></test-row> <span style="color: red;cursor: pointer" @click="handleRemove(index)">X</span> </li> </ul> </div> <script> Vue.component('test-row', { template: `<span> <span>{{ name }}:</span> <input v-model="nums"/> </span>`, props: { name: String }, data() { return { nums: '' } } }) new Vue({ el: '#app', data() { return { list: ['cat', 'dog'] } }, methods: { handleRemove(i) { this.list.splice(i, 1); } } }) </script>
出現(xiàn)問題的原因:在patch階段會認(rèn)為這兩個input子節(jié)點(diǎn)是sameVnode,進(jìn)而復(fù)用原來的dom節(jié)點(diǎn),因?yàn)榻M件只觸發(fā)了更新,沒有重新創(chuàng)建實(shí)例, 所以組件實(shí)例data數(shù)據(jù)沒有變化,輸入框內(nèi)的內(nèi)容就不會變化。
如果使用dog、cat作為key就可以避免這個bug;
以上就是vue中key使用的問題示例解析的詳細(xì)內(nèi)容,更多關(guān)于vue key使用問題的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue-cli創(chuàng)建的項(xiàng)目,配置多頁面的實(shí)現(xiàn)方法
下面小編就為大家分享一篇vue-cli創(chuàng)建的項(xiàng)目,配置多頁面的實(shí)現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03vue router動態(tài)路由設(shè)置參數(shù)可選問題
這篇文章主要介紹了vue-router動態(tài)路由設(shè)置參數(shù)可選,文中給大家提到了vue-router 動態(tài)添加 路由的方法,需要的朋友可以參考下2019-08-08vue監(jiān)聽input標(biāo)簽的value值方法
今天小編就為大家分享一篇vue監(jiān)聽input標(biāo)簽的value值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08vite+vue3+element-plus搭建項(xiàng)目的踩坑記錄
這篇文章主要介紹了vite+vue3+element-plus搭建項(xiàng)目的踩坑記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10vue-element-admin 全局loading加載等待
本文主要介紹了vue-element-admin 全局loading加載等待,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09vue使用js-file-download插件下載文件亂碼的解決
這篇文章主要介紹了vue使用js-file-download插件下載文件亂碼的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07uniapp 小程序和app map地圖上顯示多個酷炫動態(tài)的標(biāo)點(diǎn)效果(頭像后端傳過來)
這篇文章主要介紹了uniapp 小程序和app map地圖上顯示多個酷炫動態(tài)的標(biāo)點(diǎn)效果(頭像后端傳過來),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09