首先是Vue的組件的渲染方式-就地複用原則,官方的解釋是這樣的:node
因爲v-for循環渲染出的就是上面input這樣的dom元素相同,只是內容有些不一樣,因此當Vue從新渲染列表時,並不會修改每一個dom,由於修改dom的重繪和迴流時很耗時的,因此它會用元素來匹配數據項,就地更改元素,而且保證他們每一個索引位置能正確渲染。算法
這樣利用元素就地複用減小dom操做,提升了渲染速度。可是這種方式只適用於不依賴子組件狀態或臨時 DOM 狀態的列表。typescript
如:api
這個todo list的主要功能是添加,刪除todo事項,以及事項的完成和撤銷完成,並使用localStorage
在客戶端存儲數據。
寫完以後,我發現一個問題,就是當我點擊列表第一項的checkbox
表示完成這項todo時,下一項的checkbox
被自動選中(此時原來的第一項已經移動到已完成列表,這一項變成第一項),我以爲很奇怪,測試了一番又發現,在已完成列表中取消完成也會有這種狀況,總之點擊以後checkbox
的選中狀況有點錯亂。
這樣一個功能,須要修改的節點位置沒有改變,可是內容更新了,這雖然提升了複用性能,可是在複雜的表單會致使狀態出現錯位,也不會產生過分效果。那麼這種狀況怎麼解決呢?bash
key:給列表中每一項添加一個惟一的用於區分別的列表項的一個key,因此對應的value就是惟一的。dom
做用:函數
更準確
由於帶key就不是就地複用了,在sameNode函數 a.key === b.key對比中能夠避免就地複用的狀況。因此會更加準確。性能
更快
利用key的惟一性生成map對象來獲取對應節點,比遍歷方式更快測試
虛擬dom到真實dom的隱射並反映,dom節點的比較須要用到diff算法ui
咱們須要渲染真實dom的時候每每會把生成一個虛擬節點
virtual DOM,當virtual dom某個節點的數據改變後生成一個新的Vnode,而後將Vnode和oldVnode對比,固然有不一樣的地方教就直接修改在真實DOM上,而後是oldVnode=Vnode
<div>
<p>123</p>
</div>複製代碼
var Vnode={
tag:'div',
children:[{
tag:'p',text:'123'
}]
}複製代碼
在同層級進行,不會跨層級比較
oldDOM
<div>
<p>123</p>
</div>複製代碼
newDOM
<div>
<span>2222</span>
</div>複製代碼
如今咱們來看看在進行替換
function patch(oldVnode, vnode) {
// 對比是否相等
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
const oEl = oldVnode.el // 當前oldVnode對應的真實元素節點
let parentEle = api.parentNode(oEl) // 父元素
createELe(vnode) // 爲vnode生成新的元素
if (parentEle !== null) {
api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 將新的元素添加到父元素中
api.removeChild(parentEle, oldVnode.el) // 移除之前的元素
}
}
return vnode
}複製代碼
function sameVnode(a, b) {
return (
a.key === b.key && // 對比key
a.tag === b.tag && // 對比標籤名
a.isComment === b.isComment && // 是否爲註釋節點
isDef(a.data) === isDef(b.data) && // 是否認義了data,或者其餘屬性
sameInputType(a, b) //判斷是當<Input>時 是否type相同
)
}複製代碼
匹配規則
圖解