Virtual-DOM,即虚拟DOM树。浏览器在解析文件时,会将html
文档转换为document
对象,在浏览器环境中运行的脚本文件均可以获取到它,经过操做document
对象暴露的接口能够直接操做页面上的DOM节点。可是DOM读写是很是耗性能的,很容易触发没必要要的重绘和重排,为了更好地处理DOM操做,Virtual-DOM
技术就诞生了。Virtual-DOM
就是在javascript中模拟真实DOM的结构,经过数据追踪和状态对比来减小对于真实DOM的操做,以此来提升程序的效率的一种技术。javascript
Virtual-DOM
技术是前端高性能的基石,它是真实document
对象的抽象,经过对比新旧Virtual-DOM
的区别,找出发生变化的DOM节点,再利用算法获得相对更合理的DOM节点修改方案,最终再将方案应用在document
对象上来改变页面的展现内容。html
主流前端SPA框架都离不开【
Virtual-DOM
模型 +DOM-Diff
算法 + 生命周期钩子】这样的核心模型。前端
在上一篇博文《javascript基础修炼(9)——MVVM中双向数据绑定的基本原理》中,咱们经过document.getElementById()
从真实DOM中得到了带有自定义属性的待解析结构,这里是有一些问题的,实际的过程是先解析模板字符串获得虚拟DOM树,最后生成真实的DOM树。java
实际上咱们在使用SPA框架时所编写的html
模板,并无被直接当作DOM片断加载到页面上使用,而是将文件当作字符串读入到程序中,而后经过解析来生成Virtual-DOM
树,接着经过SPA框架的渲染函数来生成必要的片断后才生成真实的DOM节点。例如咱们要生成下文示例的HTML
片断(为了方便演示,示例中只涉及了类名和文本节点):算法
<body class="main"> <div class="sideBar"> <ul class="sideBarContainer"> <li class="sideBarItem">sidebar-1</li> <li class="sideBarItem">sidebar-2</li> <li class="sideBarItem">sidebar-3</li> </ul> </div> <div class="mainContent"> <div class="header">header-zone</div> <div class="coreContent">core-content</div> <div class="footer">footer-zone</div> </div> <div class="rightSide">暂未开发</div> </body>
咱们须要构建出一个简易模型来表达上面的结构:浏览器
virtualDom = { name:"body", props:{ className:"main" }, children:[{ name:"div", props:{...}, children:[...] },{ name:"div", props:{...}, children:[...] },{ name:"div", props:{...}, children:[...] }] }
创建一个生成虚拟节点的辅助函数:数据结构
//构建DOM节点的辅助函数 function h(name, props, children) { return { name:name, props:props, children:children } } //手动生成virtual-DOM var tree = h('body',{className:'main'},[ h('div',{className:'sideBar'},[ h('ul',{className:'sideBarContainer'},[ h('li',{className:'sideBarItem'},['sidebar-1']), h('li',{className:'sideBarItem'},['sidebar-2']), h('li',{className:'sideBarItem'},['sidebar-3']), ]) ]), h('div',{className:'mainContent'},[ h('div',{className:'header'},['header-zone']), h('div',{className:'coreContent'},['core-content']), h('div',{className:'footer'},['footer-zone']), ]), h('div',{className:'rightSide'},['暂未开发']) ]);
经过上面的方法获得的tree
对象就涵盖了模板片断中的结构和关键信息。实际开发中并不须要像上面同样手动来填写DOM结构,能够将模板字符串挂载到离线DOM节点上,而后在递归解析的同时来构建Virtual-DOM
就能够了。框架
至此咱们完成了模板的编译,也获得了Virtual-DOM
对象,但它彷佛并无什么用处,毕竟咱们已经完成了对模板的解析,渲染出页面没什么问题,其实Virtual-DOM
对于首屏来讲并无什么特别重要的意义,它的价值在模型和视图发生变化时才会体现。上一篇博文的末尾咱们已经提到了更新视图时的效率问题,当数据模型发生变化后,咱们须要一个方法来收集全部须要修改的DOM,并为之提供高效的修改方式(你总不能一有变化就把整个网页从新渲染,或者让数据模型各自去修改各自绑定的DOM吧)。那么为了可以收集全部DOM节点的变化,咱们就须要遍历全部节点。dom
对数据结构和算法有必定了解的读者很容易想到,遍历解析一个Virtual-DOM
实际上就是对其进行先序深度优先遍历(Pre-Order Depth-First-Search),本节中,咱们先预热一下,使用这种方式来复现一下DOM结构。数据结构和算法
function dfswalking(tree) { var _childrenLength; //执行动做 if (typeof tree.children[0] === 'string') { console.log(`<${tree.name} class="${tree.props.className}">${tree.children[0]}</${tree.name}>`); } else { console.log(`<${tree.name} class="${tree.props.className}"> -->`); for(var i = 0, _childrenLength = tree.children.length; i < _childrenLength; i++){ dfswalking(tree.children[i]); } } }
本例中仅打印出字符串的方式来展现,能够在控制台看到输出结果:
下一篇博文中将分析如何经过domDiff(oldTree, newTree)
的方法经过一样的遍历方法来收集变化并批量更新视图。
本篇只是部分原理的学习笔记,并不表明框架真实源码的实现逻辑。