首先认识两个概念:
VNode
VNode全称Virtual Node,也就是虚拟节点,无论是组件还是元素,最终在Vue中表现出来的都是一个个VNode,VNode的本质是一个JavaScript对象。VNode最大的好处是做多平台适配,比如服务端渲染,移动端渲染,浏览器渲染。
虚拟DOM
简单来说就是用JS模拟DOM结构,虚拟DOM是一个JS对象。一大堆元素会形成一个VNode Tree,VNode的本质就是用树型结构的JS对象来描述真实的DOM结构的信息,这个树结构的JS对象包含了整个DOM结构的信息。
//真实DOM
<ul id="list">
<li class="item">你好</li>
<li class="item">不错</li>
</ul>
//虚拟DOM
let VDOM = {
tagName:'ul',
props:{
id:'list'
},
children:[
{
tagName:'li',props:{ class:'item'},children:['你好']
},
{
tagName:'li',props:{ class:'item'},children:['不错']
}
]
}
数据改变 -> 虚拟DOM(计算变更)-> 操作真实DOM -> 视图更新
diff算法
上面的例子中,如果只改变其中一个li标签的值,diff算法会查出来这个改变的li标签,来更新DOM。
diff算法通过对比新旧虚拟DOM,找出改变的虚拟节点,然后只更新这个虚拟节点对应的真实节点,更新真实DOM,更加精准,也提高了效率。
v-for中没有key值会怎么办?
根据vue.js源码,没有key值的话:
- 会遍历节点数小的,节点值相等的不修改,复用;
- 节点值不相等的修改为新节点的值;
- 如果旧节点数大于新节点数,删除剩余的旧节点;如果新节点数大于旧节点数,创建新的节点。
这种方式性能比较低。
v-for中有key值怎么办?
- 从头部开始遍历,节点相同(类型和值都相同),执行patch,继续遍历,节点不同,跳出循环break;
- 从尾部开始遍历,节点相同,执行patch,继续遍历,节点不同,跳出循环break;
- 如果旧节点遍历完了, 依然有新的节点, 那么新的节点就是添加(mount);
- 如果新的节点遍历完了, 还有旧的节点, 那么旧的节点就是移除的;
- 如果中间存在不知道如何排序的位置序列,那么就使用key建立索引图,最大限度使用旧节点。
所以发现,Vue在进行diff算法的时候,会尽量利用key进行优化操作:
- 在没有key的时候效率非常低;
- 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加高效。