如何理解vue中的key?

就目前所瞭解的狀況,key的做用有如下這些。html

  • v-for遍歷時,用id,uuid之類做爲key,惟一標識節點加速虛擬DOM渲染
  • 響應式系統沒有監聽到的數據,用+new Date()生成的時間戳做爲key,手動強制觸發從新渲染

場景一大同小異司空見慣,場景二是下面這樣的:vue

<div :key="rerender">
    <span>Hello Vue.js !</span>
    <complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>

refresh(){
    this.rerender = + new Date();
}

那麼vue中key的相關知識點究竟是怎樣的呢?node

  • 官方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的移動,而且會嘗試盡最大程度在同適當的地方對相同類型的element,作patch或者reuse。
  • 若是使用了key,Vue會根據keys的順序記錄element,曾經擁有了key的element若是再也不出現的話,會被直接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

  • 觸發組件的lifecycle
  • 觸發transition
<transition>
  <span :key="text">{{ text }}</span>
</transition>

text發生變化時,<span>會被replaced,而不會patched,所以transition會被觸發。
個人理解:
text變化時,span的key發生了變化,也就是說曾經擁有了舊key的span再也不出現了,當擁有新值的text做爲key時,擁有了新key的span出現了,那麼舊key span會被移除,舊transition也會移除,新key span觸發渲染,新transition觸發。git

上面2個使用場景背後的原理是什麼?

結合官方API的知識點,如今再來回顧文章開頭提出的場景。github

場景一:v-for遍歷時,用id,uuid之類做爲key,惟一標識節點加速虛擬DOM渲染

答案:算法

  • 若是不用key,Vue會用一種算法:最小化element的移動,而且會嘗試盡最大程度在同適當的地方對相同類型的element,作patch或者reuse。
  • 若是使用了key,Vue會根據keys的順序記錄element,曾經擁有了key的element若是再也不出現的話,會被直接remove或者destoryed。

場景二:響應式系統沒有監聽到的數據,用+new Date()生成的時間戳做爲key,手動強制觸發從新渲染

<div :key="rerender">
    <span>Hello Vue.js !</span>
    <complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>

refresh(){
    this.rerender = + new Date();
}

答案:api

  • 若是使用了key,Vue會根據keys的順序記錄element,曾經擁有了key的element若是再也不出現的話,會被直接remove或者destoryed。
  • refresh方法調用後,包含了span和complexComponent的div的key發生了變化,也就是說曾經擁有了舊key的div再也不出現了,當擁有新值的rerender做爲key時,擁有了新key的div出現了,那麼舊key div會被移除,舊span和complexComponent也會移除,新key div觸發渲染,新span,帶着父組件新propObj和propArr的新complexComponent渲染。

思考:數組

  1. 爲何要叫propObj和propArr?
  2. 帶着父組件新propObj和propArr的新complexComponent渲染。 爲何要加粗?

因爲Vue.js的obj和arr存在沒法檢測到數據變化的狀況,obj是屬性的新增和刪除(緣由是新增和刪除都沒有觸發setter,watcher未告訴外界更新),arr則是數組內元素從新賦值或者修改length屬性(緣由是沒有使用改變數組自己的方法,沒有觸發數組原型鏈攔截器,watcher未告訴外界更新)。
因此!經過賦予新key的方式,移除舊key div,渲染新key div,propObj和propArr在complexComponent組件內會從新觸發一次生命週期,作一次從新渲染。此時父組件的propObj和propArr js變量其實已經獲取到新值了,只是沒有觸發DOM也好,VNode也好的從新渲染。須要經過刷新key去force update,說到forceUpdate,能夠經過$forceUpdate()去手動強制更新DOM。ide

除key外,還有其它強制更新DOM的方法嗎?

場景:父組件修改傳遞給子組件的數據,數組數據的更新沒有按照this.$set去更新。該怎麼辦?ui

this.productImages.forEach((product) => {
  if (product.productId in this.productsState) {
    product.status = this.productsState[product.productId];
  }
});

不使用this.$set去賦值數據的不能rerender的緣由是什麼?
在Vue.js中,對Array的變化偵測是經過攔截原型的方式實現的。也就經過對push,pop,shift,unshift,splice,sort,reverse,fill,copyWithin去改變數組自身內容的方法作攔截,從而響應。而product.status = this.productsState[product.productId];沒有觸發任何改變數組自身的被監聽的方法,所以不會rerender。

  • 刷新組件的key
  • $forceUpdate方法

刷新組件的key

1.這個key加在什麼地方比較好?

加在this.productImages的父元素上就好。
若不涉及數據傳遞,也能夠直接加在須要更新的element上。

2.用什麼作key值?

如今是粗暴的+new Date()時間戳作key值的。
也能夠用雙向綁定的值做爲key值,保證新舊key值不一樣就行。

3.key的原理是什麼?

vue.js的虛擬DOM算法,在更新vNode時,須要從舊vNode列表中查找與新vNode節點相同的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已經發生變化了,可是Vue.js數組響應式的實現因爲是攔截原型鏈方法的方式,沒有檢測到這個變化,因此不會自動rerender,沒有觸發update。所以咱們經過$forceUpdate的方式,調用包含dep的watcher上的update方法,從而作到rerender

2.能夠在子組件監聽事件,父組件發送事件而後只刷新子組件嗎?

不能夠。
由於dep是父組件的watcher和dep,並非子組件,是父組件的this.productImages沒有被檢測到並實時更新,並非子組件的問題。

參考資料

https://vuejs.org/v2/api/#key
https://vuejs.org/v2/api/#vm-...
https://vuejs.org/v2/guide/co...

原文連接:https://github.com/FrankKai/F...

相關文章
相關標籤/搜索