diff算法
diff算法:
在之前的更新中,每次数据更新,在更新视图时,都是完全产生新的虚拟节点,通过新的虚拟节点生成真实节点,用新生成的真实节点替换所有的老节点。
这种方法在页面元素很少的情况下性能销毁倒是无所谓,但是在页面元素特别多情况下,很明显是消耗很大性能的。哪怕我只是修改了一个dom的文本内容,也都需要重新生成一遍所有节点。(因为现在只有一个组件)
第一次渲染的时候,我们会产生虚拟节点,第二次更新我们也会调用render方法产生新的虚拟节点,我们需要对比两次的vnode,找到需要更新的部分进行更新。
diff的实现,我是建立在前面的代码上的。
实现mini-vue之 computed,watch ,数组响应式的实现
没有key
对于没有key的情况下:vue会在两个vnode的tag相同的时候,就任务是同一个节点。这种情况下可能会出现错误复用。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<!--更新后-->
<ul>
<li>2</li>
<li>3</li>
<li>1</li>
</ul>
此时vue只会让第一个节点和第一个节点比较,第二个节点和第二个节点比较。
有key
vue在进行diff的时候(新旧虚拟dom都有子节点数组),维护了一个双指针,来进行比较。
// 我们为了比较两个儿子的时候,提高比较的性能(速度)
/**
* 1. 我们操作列表 经常会有 push pop shift unshift sort reverse 等方法 针对这些情况可以做一些优化
* 2. vue2中采用双指针的方法 比较两个节点
*/
let oldStartIndex = 0,
oldEndIndex = oldChildren.length - 1,
newStartIndex = 0,
newEndIndex = newChildren.length - 1,
oldStartVnode = oldChildren[oldStartIndex],
oldEndVnode = oldChildren[oldEndIndex],
newStartVnode = newChildren[newStartIndex],
newEndVnode = newChildren[newEndIndex];
old head -> new head
新旧节点都进行头指针指向的头结点比较。如果两个子节点相同,则会进行复用。
<ul>
<li key="a">1</li>
<li key="b">2</li>
<li key="c">3</li>
</ul>
<!--更新后-->
<ul>
<li key="a">1</li>
<li key="b">2</li>
<li key="d">4</li>
</ul>
此时vue会复用前两个节点(比对后发现前两个节点都不需要更改),只需要在原来的dom元素上追加一个子元素而已。
old tail -> new tail
在头结点进行比较时,发现不是一个节点,则再次比较两个children的尾节点。
<ul>
<li key="a">1</li>
<li key="b">2</li>
<li key="c">3</li>
</ul>
<!--更新后-->