阅读本文前请先了解组件更新的原理。 “组件更新-新旧节点相同”部分
我们的用例如图所示,您可以添加新项目并且可以选中复选框,代码如下:
<div id="app">
<div>
<input type="text" v-model="name" />
<button @click="add">添加</button>
</div>
<ul>
<li v-for="(item, i) in list">
<input type="checkbox" /> {{item.name}}
</li>
</ul>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "",
newId: 3,
list: [
{ id: 1, name: "李斯" },
{ id: 2, name: "吕不韦" },
{ id: 3, name: "嬴政" },
],
},
methods: {
add() {
//注意这里是unshift
this.list.unshift({ id: ++this.newId, name: this.name });
this.name = "";
},
},
});
</script>
页面是这样的,先选择一项
发现之前选择的“李斯”没有被选中,而是新添加的“妲己”被选中。
老ulvnode的孩子是3里vnode
ulvnode的新孩子有4里vnode,又增加了一个新的“妲己”
旧li的孩子的“oldCh[0].children[0].checked”复选框值为true,因为它被选中,而新li的孩子的checkbox值还不存在,所以它是未定义的。不会创建真实圆顶元素或虚拟圆顶状态。
在第430行执行,oldStartVnode和newStartVnode,
所以vnode的递归比较和更新就是这两种方法。更新在 patchNode 中,子级的比较顺序在 updateChildren 中。
将旧复选框对象的真实房屋分配给新的 vnode Elm 复选框。所以渲染到实际的DOM树时,会选中第一个li标签,即“妲己”,并且选中里面的子元素的复选框。
继续执行,没有任何改变dom值的操作,都是钩子和回调。其实实际房子的修改也在这个方法中,比如addVnodes、removeVnodes、setTextContent等。
执行完毕后,返回到上一个方法——updateChildren。还记得 while 循环吗?重点是将孩子们一一匹配成一个圆圈。比较完 li 下的复选框后,现在比较 item_name 文本节点。还是按照比较规则,执行到第430行,然后进入patchNode,这次到了第569行,由于这是一个文本节点,所以上面的判断是Undef(vnode.text),这是一个文本节点,是有定义的,所以else if 分支将被删除。由于“李斯”和“妲己”不一样,所以“李斯”将被“妲己”取代。
于是新添加的“妲己”出现并被选中。
现在当你添加一个键并使用 id 作为键时,会发生什么?
因为比较的时候用if sameNode来判断是否是同一个节点。其中一个明喻是关键,
会判断不是同一个节点,执行不同的节点操作,“创建新节点->更新代理节点->删除旧节点逻辑”并执行第718行else分支。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。