但是要注意的是,因为tagName是可重复的,不能用这个来进行对比。所以需要给子节点加上唯一标识key,列表对比的时候,使用key进行对比,这样才能复用老的 DOM 树上的节点。这样,我们就可以通过深度优先遍历两棵树,每层的节点进行对比,记录下每个节点的差异了。
接下来就是第三步,把差异应用到真正的DOM树上!因为步骤一所构建的 JavaScript 对象树和render出来真正的DOM树的信息、结构是一样的。所以我们可以对那棵DOM树也进行深度优先的遍历,遍历的时候从步骤二生成的patches对象中找出当前遍历的节点差异,然后进行 DOM 操作。
1 function patch (node, patches) { 2 var walker = {index: 0} 3 dfsWalk(node, walker, patches) 4 } dfsWalk (node, walker, patches) { len = node.childNodes 10 ? node.childNodes.length 11 : 0 child = node.childNodes[i] 14 walker.index++ 15 dfsWalk(child, walker, patches) 16 } (currentPatches) { } 21 }
applyPatches,根据不同类型的差异对当前节点进行 DOM 操作:
1 function applyPatches (node, currentPatches) { 2 currentPatches.forEach(function (currentPatch) { 3 switch (currentPatch.type) { 4 case REPLACE: 5 node.parentNode.replaceChild(currentPatch.node.render(), node) REORDER: 8 reorderChildren(node, currentPatch.moves) PROPS: 11 setProps(node, currentPatch.props) TEXT: 14 node.textContent = currentPatch.content : Error('Unknown patch type ' + currentPatch.type) 18 } 19 }) 20 }
而virtual DOM 算法主要是实现上面步骤的三个函数:element,diff,patch。然后就可以实际的进行使用:
tree = el('div', {'id': 'container'}, [ 3 el('h1', {style: 'color: blue'}, ['simple virtal dom']), 4 el('p', ['Hello, virtual-dom']), 5 el('ul', [el('li')]) 6 ]) root = tree.render() 10 document.body.appendChild(root) newTree = el('div', {'id': 'container'}, [ 14 el('h1', {style: 'color: red'}, ['simple virtal dom']), 15 el('p', ['Hello, virtual-dom']), 16 el('ul', [el('li'), el('li')]) 17 ]) patches = diff(tree, newTree) patch(root, patches)
至此,全部的渲染过程都完成了,到此为止我想很多新入手的同学多少应该可以理解这个React了这篇博客中的算法实现我也是参考Github上面一位大神的实现,所以在这里我更多的是介绍整个流程的思路,希望对于React还一头雾水的同学能够仔细看过这篇说明后有所理解,不是算法不重要,无论是哪个岗位上的程序员,都需要熟悉数据结构,这是作为一个程序员理清思路的必备要素~~接下来的博客我会介绍一些前端工程化上面的理解,希望大家能喜欢~~