Virtual-DOM在Vue中的实现--snabbdom源码解读
vue在2.0版本中引入了virtual-DOM,其实现复刻了开源库snabbdom,最近看了一下snabbdom的源码。snabbdom从0.6.0版本开始使用了TypeScript,本文使用的是0.5.4使用JavaScript的版本。
VNode的定义
1 | module.exports = function(sel, data, children, text, elm) { |
h参数/createElement
1 | module.exports = function h(sel, b, c) { |
snabbdom的patch解析
virtual dom diff
1 | return function(oldVnode, vnode) { |
patch过程
1 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) { |
updateChildren
1 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { |
我们先遍历两个数组(while语句),维护四个变量
- 遍历oldCh的头索引 - oldStartIdx
- 遍历oldCh的尾索引 - oldEndIdx
- 遍历newCh的头索引 - newStartIdx
- 遍历newCh的尾索引 - newEndIdx
当oldStartIdx > oldEndIdx或者newStartIdx > newOldStartIdx的时候停止遍历。
遍历过程中有五种比较
前四种比较
- oldStartVnode和newStartVnode,两者elm相对位置不变,若值得(sameVnode)比较,这patch这两个vnode
- oldEndVnode和newEndVnode,同上,elm相对位置不变,做相同patch检测
- oldStartVnode和newEndVnode,如果oldStartVnode和newEndVnode值得比较,说明oldCh中的这个- oldStartVnode.elm向右移动了。那么执行api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm))调整它的位置
- oldEndVnode和newStartVnode,同上,但这是oldVnode.elm向左移,需要调整它的位置
最后一种比较
利用vnode.key,在ul>li*n的结构里,我们很有可能使用key来标志li的唯一性,那么我们就会来到最后一种情况。这个时候,我们先产生一个index-key表(createKeyToOldIdx),然后根据这个表来进行更改。
更改规则
- 如果newVnode.key不在表中,那么这个newVnode就是新的vnode,将其插入
- 如果newVnode.key在表中,那么对应的oldVnode存在,我们需要patch这两个vnode,并在patch之后,将这个oldVnode置为undefined(oldCh[idxInOld] = undefined),同时将oldVnode.elm位置变换到当前oldStartIdx之前,以免影响接下来的遍历
遍历结束后,检查四个变量,对移除剩余的oldCh或添加剩余的newCh
参考文献
- 一个virtual-dom-library。vue中virtual DOM是复刻的该实现:snabbdom
- React-less Virtual DOM with Snabbdom :functions everywhere!snabbdom介绍