初探虚拟 DOM
栏目: JavaScript · 发布时间: 6年前
内容简介:如果有这么一张表格要你维护。后续涉及到表的增删改,你会怎么做?表格简单的时候还好,用 JavaScript 操作起来还算方便。但随着应用越来越复杂,需要处理的数据也越来越大,越来越复杂的时候,需要利用 JavaScript 操作的地方也会越来越多,这个时候准确地修改数据就变得不是那么容易了。
如果有这么一张表格要你维护。
后续涉及到表的增删改,你会怎么做?
- 增:先找到正确的位置,再插元素进去?
- 删:找到正确的元素,删掉它?
- 改:找到正确的元素,修改它?
表格简单的时候还好,用 JavaScript 操作起来还算方便。但随着应用越来越复杂,需要处理的数据也越来越大,越来越复杂的时候,需要利用 JavaScript 操作的地方也会越来越多,这个时候准确地修改数据就变得不是那么容易了。
虚拟 DOM 的产生
针对前面的情况,那么能不能用一个东西来存储页面的视图状态,当视图状态发送变化时,读取这个东西,然后更新页面?
比如这一段 HTML 代码对应的 DOM,
<div> <div> <span>hello</span> </div> <span>world</span> </div> 复制代码
我们用另外的一个对象来表示它
let nodesData = { tag: 'div', children: [ { tag: 'div', children: [ { tag: 'span', children: [ { tag: '#text', text: 'hello' } ] } ] }, { tag: 'span', children: [ { tag: '#text', text: 'world' } ] } ] } 复制代码
用这个对象来表示 DOM 结构,我们可以根据这个对象来构建真正的 DOM。
现在我们需要写一个函数,将这个虚假的 DOM 转化为真实的 DOM。
化假为真
function vNode({tag, children, text}){ this.tag = tag this.children = children this.text = text } vNode.prototype.render = function(){ if(this.tag === '#text'){ return document.createTextNode(this.text) } let el = document.createElement(this.tag) this.children.map((vChild) => { el.appendChild(new vNode(vChild).render()) }) return el } 复制代码
调用上面的这个函数可以将我们用来表示 DOM 的对象(虚假 DOM)变成真正的 DOM。
let node = new vNode(nodesData) node.render() 复制代码
这样,就化假 DOM 为真 DOM 了。
当我们的需要改变 DOM 时,只需要改变其对应的虚假 DOM,再调用一下 render 函数,就可以改变真实 DOM,不需要我们亲自用 JavaScript 去操作页面中的 DOM。
局部更新
上面虽然实现了从虚假 DOM 到真实 DOM 的转化,但是也有一个问题,那就是每次转化都会遍历所有的 DOM 结构,通通的全部转化一遍。如果只有一个小地方发生了改变,也需要将全部的 DOM 更新一遍,那这样就太耗费性能了,我们应该比较虚假 DOM 的变化,只更新变化的地方。
function patchElement(parent, newVNode, oldVNode, index = 0) { if (!oldVNode) { parent.appendChild(newVNode.render()); } else if (!newVNode) { parent.removeChild(parent.childNodes[index]); } else if (newVNode.tag !== oldVNode.tag || newVNode.text !== oldVNode.text) { parent.replaceChild(new vNode(newVNode).render(), parent.childNodes[index]); } else { for ( let i = 0; i < newVNode.children.length || i < oldVNode.children.length; i++ ) { patchElement( parent.childNodes[index], newVNode.children[i], oldVNode.children[i], i ); } } } 复制代码
通过这个算法,逐层比较新旧虚假 DOM 的结构变化,如果没变,就继续往下遍历;如果发现结构发生了变化,就重新生成真实 DOM 替换掉旧的。
来看一看效果。
从图中可以看到,当虚假 DOM 发生变化时,在更新真实 DOM 的过程中,只更新了发生了变化的那一部分,没有发生变化的地方是没动的,这样就优化了性能。
结语
这是一个非常粗糙的实现,diff 算法非常简单地比较了差异,这里仅仅表达了一下虚拟 DOM 的实现思想,在实际运用过程还有很多地方需要考虑。
这里贴个完整代码。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title> </head> <body> <div id="test"></div> <script> let nodesData = { tag: 'div', children: [ { tag: 'div', children: [ { tag: 'span', children: [ { tag: '#text', text: 'hello' } ] } ] }, { tag: 'span', children: [ { tag: '#text', text: 'world' } ] } ] }; let nodesData2 = { tag: 'div', children: [ { tag: 'div', children: [ { tag: 'span', children: [ { tag: '#text', text: 'HELLO' } ] } ] }, { tag: 'span', children: [ { tag: '#text', text: 'WORLD' } ] } ] }; function vNode({ tag, children, text }) { this.tag = tag; this.children = children; this.text = text; } vNode.prototype.render = function () { if (this.tag === '#text') { return document.createTextNode(this.text); } let el = document.createElement(this.tag); this.children.map(vChild => { el.appendChild(new vNode(vChild).render()); }); return el; }; function patchElement(parent, newVNode, oldVNode, index = 0) { if (!oldVNode) { parent.appendChild(newVNode.render()); } else if (!newVNode) { parent.removeChild(parent.childNodes[index]); } else if (newVNode.tag !== oldVNode.tag || newVNode.text !== oldVNode.text) { parent.replaceChild(new vNode(newVNode).render(), parent.childNodes[index]); } else { for ( let i = 0; i < newVNode.children.length || i < oldVNode.children.length; i++ ) { patchElement( parent.childNodes[index], newVNode.children[i], oldVNode.children[i], i ); } } } let node1 = new vNode(nodesData); let node2 = new vNode(nodesData2); let test = document.querySelector('#test'); test.appendChild(node1.render()); setTimeout(() => { patchElement(test, node2, node1, 0); }, 5000); </script> </body> </html> 复制代码
以上所述就是小编给大家介绍的《初探虚拟 DOM》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
用户力:需求驱动的产品、运营和商业模式
郝志中 / 机械工业出版社 / 2015-11-1 / 59.00
《用户力:需求驱动的产品、运营和商业模式》从用户需求角度深刻阐释了互联网产品设计、网络运营、商业模式构建的本质与方法论! 本书以“用户需求”为主线,先用逆向思维进行倒推,从本质的角度分析了用户的需求是如何驱动企业的产品设计、网络运营和商业模式构建的,将这三个重要部分进行了系统性和结构化的串联,然后用顺向思维进行铺陈,从实践和方法论的角度总结了企业究竟应该如围绕用户的真实需求来进行产品设计、网......一起来看看 《用户力:需求驱动的产品、运营和商业模式》 这本书的介绍吧!