如何正確理解vue中的key詳解
就目前所了解的情況,key的作用有以下這些。
- v-for遍歷時,用id,uuid之類作為key,唯一標識節(jié)點加速虛擬DOM渲染
- 響應式系統(tǒng)沒有監(jiān)聽到的數(shù)據(jù),用+new Date()生成的時間戳作為key,手動強制觸發(fā)重新渲染
場景一大同小異司空見慣,場景二是下面這樣的:
<div :key="rerender">
<span>Hello Vue.js !</span>
<complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>
refresh(){
this.rerender = + new Date();
}
那么vue中key的相關知識點到底是怎樣的呢?
- 官方API知識點
- 上面2個使用場景背后的原理是什么?
- 除key外,還有其它強制更新DOM的方法嗎?
- 參考資料
官方API知識點
- 在Vue.js中,key是6個特殊屬性key, ref, is, slot, slot-scope, scope其中之一。
- key的值可以是number,也可以是string。
- key主要作用于Vue的virtual DOM算法,在diff new nodes list和old nodes list時,作為識別VNode的一個線索。
- 如果不用key,Vue會用一種算法:最小化element的移動,并且會嘗試盡最大程度在同適當?shù)牡胤綄ο嗤愋偷膃lement,做patch或者reuse。
- 如果使用了key,Vue會根據(jù)keys的順序記錄element,曾經(jīng)擁有了key的element如果不再出現(xiàn)的話,會被直接remove或者destoryed。
- 擁有同一個parent的children必須有unique keys。重復的key的導致render error。
最常用的用法一:v-for
<ul> <li v-for="item in items" :key="item.id">...</li> </ul>
最常用的用法二:強制替換element或者component
- 觸發(fā)組件的lifecycle
- 觸發(fā)transition
<transition>
<span :key="text">{{ text }}</span>
</transition>
text發(fā)生變化時,<span>會被replaced,而不會patched,因此transition會被觸發(fā)。
我的理解:
text變化時,span的key發(fā)生了變化,也就是說曾經(jīng)擁有了舊key的span不再出現(xiàn)了,當擁有新值的text作為key時,擁有了新key的span出現(xiàn)了,那么舊key span會被移除,舊transition也會移除,新key span觸發(fā)渲染,新transition觸發(fā)。
上面2個使用場景背后的原理是什么?
結合官方API的知識點,現(xiàn)在再來回顧文章開頭提出的場景。
場景一:v-for遍歷時,用id,uuid之類作為key,唯一標識節(jié)點加速虛擬DOM渲染
答案:
- 如果不用key,Vue會用一種算法:最小化element的移動,并且會嘗試盡最大程度在同適當?shù)牡胤綄ο嗤愋偷膃lement,做patch或者reuse。
- 如果使用了key,Vue會根據(jù)keys的順序記錄element,曾經(jīng)擁有了key的element如果不再出現(xiàn)的話,會被直接remove或者destoryed。
場景二:響應式系統(tǒng)沒有監(jiān)聽到的數(shù)據(jù),用+new Date()生成的時間戳作為key,手動強制觸發(fā)重新渲染
<div :key="rerender">
<span>Hello Vue.js !</span>
<complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>
refresh(){
this.rerender = + new Date();
}
答案:
- 如果使用了key,Vue會根據(jù)keys的順序記錄element,曾經(jīng)擁有了key的element如果不再出現(xiàn)的話,會被直接remove或者destoryed。
- refresh方法調用后,包含了span和complexComponent的div的key發(fā)生了變化,也就是說曾經(jīng)擁有了舊key的div不再出現(xiàn)了,當擁有新值的rerender作為key時,擁有了新key的div出現(xiàn)了,那么舊key div會被移除,舊span和complexComponent也會移除,新key div觸發(fā)渲染,新span,帶著父組件新propObj和propArr的新complexComponent渲染。
思考:
- 為什么要叫propObj和propArr?
- 帶著父組件新propObj和propArr的新complexComponent渲染。 為什么要加粗?
由于Vue.js的obj和arr存在無法檢測到數(shù)據(jù)變化的情況,obj是屬性的新增和刪除(原因是新增和刪除都沒有觸發(fā)setter,watcher未告訴外界更新),arr則是數(shù)組內(nèi)元素重新賦值或者修改length屬性(原因是沒有使用改變數(shù)組本身的方法,沒有觸發(fā)數(shù)組原型鏈攔截器,watcher未告訴外界更新)。
所以!通過賦予新key的方式,移除舊key div,渲染新key div,propObj和propArr在complexComponent組件內(nèi)會重新觸發(fā)一次生命周期,做一次重新渲染。此時父組件的propObj和propArr js變量其實已經(jīng)獲取到新值了,只是沒有觸發(fā)DOM也好,VNode也好的重新渲染。需要通過刷新key去force update,說到forceUpdate,可以通過$forceUpdate()去手動強制更新DOM。
除key外,還有其它強制更新DOM的方法嗎?
場景:父組件修改傳遞給子組件的數(shù)據(jù),數(shù)組數(shù)據(jù)的更新沒有按照this.$set去更新。該怎么辦?
this.productImages.forEach((product) => {
if (product.productId in this.productsState) {
product.status = this.productsState[product.productId];
}
});
不使用this.$set去賦值數(shù)據(jù)的不能rerender的原因是什么?
在Vue.js中,對Array的變化偵測是通過攔截原型的方式實現(xiàn)的。也就通過對push,pop,shift,unshift,splice,sort,reverse,fill,copyWithin去改變數(shù)組自身內(nèi)容的方法做攔截,從而響應。而product.status = this.productsState[product.productId];沒有觸發(fā)任何改變數(shù)組自身的被監(jiān)聽的方法,因此不會rerender。
- 刷新組件的key
- $forceUpdate方法
刷新組件的key
1.這個key加在什么地方比較好?
加在this.productImages的父元素上就好。
若不涉及數(shù)據(jù)傳遞,也可以直接加在需要更新的element上。
2.用什么做key值?
現(xiàn)在是粗暴的+new Date()時間戳做key值的。
也可以用雙向綁定的值作為key值,保證新舊key值不同就行。
3.key的原理是什么?
vue.js的虛擬DOM算法,在更新vNode時,需要從舊vNode列表中查找與新vNode節(jié)點相同的vNode進行更新,如果這個過程設置了屬性key,過程就會快很多。
其他具體見上文。
$forceUpdate方法
只能在父組件調用這個方法,手動通知vue實例重新渲染。
// $forceUpdate源碼
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
// update源碼
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
1.$forceUpdate可以更新的原理分析
product.status = this.productsState[product.productId];以后,其實此時dep已經(jīng)發(fā)生變化了,但是Vue.js數(shù)組響應式的實現(xiàn)由于是攔截原型鏈方法的方式,沒有檢測到這個變化,所以不會自動rerender,沒有觸發(fā)update。因此我們通過$forceUpdate的方式,調用包含dep的watcher上的update方法,從而做到rerender。
2.可以在子組件監(jiān)聽事件,父組件發(fā)送事件然后只刷新子組件嗎?
不可以。
因為dep是父組件的watcher和dep,并不是子組件,是父組件的this.productImages沒有被檢測到并實時更新,并不是子組件的問題。
參考資料
https://vuejs.org/v2/api/#key
https://vuejs.org/v2/api/#vm-...
https://vuejs.org/v2/guide/co...
總結
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。
相關文章
Vue使用mounted和created時,this無法指向data中的數(shù)據(jù)問題
這篇文章主要介紹了Vue使用mounted和created時,this無法指向data中的數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
Vue+Echarts實現(xiàn)繪制多設備狀態(tài)甘特圖
這篇文章主要為大家詳細介紹了Vue如何結合Echarts實現(xiàn)繪制多設備狀態(tài)甘特圖,文中的示例代碼講解詳細,有需要的小伙伴可以跟隨小編一起學習一下2024-03-03
詳解Vue路由History mode模式中頁面無法渲染的原因及解決
這篇文章主要介紹了詳解Vue路由History mode模式中頁面無法渲染的原因及解決,非常具有實用價值,需要的朋友可以參考下2017-09-09
vue3+高德地圖只展示指定市、區(qū)行政區(qū)域的地圖以及遮罩反向鏤空其他地區(qū)
vue大屏項目開發(fā),客戶覺得地圖上的文字標注太多了,要求地圖上只顯示省市等主要城市的標注,這篇文章主要給大家介紹了關于vue3+高德地圖只展示指定市、區(qū)行政區(qū)域的地圖以及遮罩反向鏤空其他地區(qū)的相關資料,需要的朋友可以參考下2024-02-02

