当前位置: 首页 > Web前端 > vue.js

Vue-VirtualDOM

时间:2023-04-01 12:43:01 vue.js

Vue-VirtualDOM一、模板转换为视图的过程Vue.js通过编译将模板模板转换为渲染函数(h),执行渲染函数得到虚拟节点树.当对Model进行操作时,会触发对应Dep中的Watcher对象。Watcher对象会调用相应的更新来修改视图。这个过程主要是比较新旧虚拟节点的差异,然后根据比较结果进行DOM操作更新视图。二、VirtualDOM1.定义VirtualDOM实际上是一棵基于VNode节点的树,使用对象属性来描述节点。实际上,它只是对真实DOM的抽象。最后,这棵树可以通过一系列操作映射到真实环境中。简单来说,VirtualDOM可以理解为一个简单的JS对象,其中有几个重要的属性:tag属性就是这个vnode的tag属性,data属性包含finally之后节点上的class和attribute渲染成一个真实的dom节点,style和boundeventchildren属性是vnode的子节点text属性是text属性elm属性是这个vnode对应的真实dom节点key属性key属性是vnode的标记,它在diff过程中可以提高diff的效率,后面会讲到虚拟DOM,我们看一个简单的例子,就是下图这个,里面详细解释了模板→渲染函数→的过程virtualDOMtree→realDOM2.作用于virtualDOM的最终目的是将虚拟节点转化为Rendered到view。但是如果直接用虚拟节点覆盖旧节点,就会有很多不必要的DOM操作。比如一个ul标签下有很多li标签,只有一个li变了。在这种情况下,如果使用新的ul来替换旧的ul,就会因为这些不必要的DOM操作而浪费性能。为了避免不必要的DOM操作,虚拟DOM在将虚拟节点映射到视图的过程中,将虚拟节点与上次渲染视图的旧虚拟节点(oldVnode)进行比较,找出真正需要的节点为DOM操作更新,避免操作其他不需要改变的DOM。实际上,虚拟DOM在Vue.js中主要做了两件事:**提供真实DOM节点对应的虚拟节点vnode,将虚拟节点vnode与旧虚拟节点oldVnode进行比较,然后更新视图**3。优点:具有跨平台优势:由于VirtualDOM基于JavaScript对象,不依赖真实平台环境,具有跨平台能力,如浏览器平台、Weex、Node等,DOM运行速度慢,且js运行效率高。我们可以把DOM比较操作放在JS层来提高效率:因为DOM操作的执行速度远比Javascript慢,所以大量的DOM操作转移到Javascript,使用patchingAlgorithm来计算节点确实需要更新,最大限度地减少DOM操作,从而显着提高性能。提升渲染性能:VirtualDOM的优势不在于单一的操作,而是能够在大量频繁的数据更新下合理高效地更新视图。3.Diff算法Vue的diff算法是在snabbdom的基础上修改的。它只做同级vnode之间的diff,递归执行同级vnode的diff,最终实现整个DOM树的更新。由于跨层操作很少,可以忽略不计,时间复杂度从O(n3)变为O(n)。diff算法包括几个步骤:使用JavaScript对象结构来表示DOM树的结构;然后用这棵树构建一个真正的DOM树,当状态改变时将它插入到文档中,重建一个新的对象树。然后将新树与旧树进行比较,记录两棵树的差异,将记录的差异应用到构建的真实DOM树上,视图就会更新。h)constroot=document.getElementById('root');constoldVnode=h('ul',{id:'container'},h('li',{style:{backgroundColor:'#110000'},键:'A'},'A'),h('li',{style:{backgroundColor:'#440000'},key:'B'},'B'),h('li',{style:{backgroundColor:'#770000'},key:'C'},'C');constnewVnode=h('ul',{id:'newContainer'},h('li',{style:{backgroundColor:'#440000'},key:'B'},'B1'),h('li',{style:{backgroundColor:'#110000'},key:'A'},'A1'),h('li',{style:{backgroundColor:'#AA0000'},key:'C'},'C1'),//h('li',{style:{backgroundColor:'#AA0000'},key:'E'},'E1'));mount(oldVnode,root);setTimeout(()=>{patch(oldVnode,newVnode);},1000)2.通过渲染函数转换为虚拟DOM树函数h(type,props,...children){letkey,newProps={};if(props){if(props.key){key=props.key;删除props.key;}for(letiteminprops){if(props.hasOwnProperty(item)){newProps[item]=props[item];}}}returnvnode(type,key,props,children.map(child=>{//处理文本节点if(typeofchild=='string'||typeofchild=='number'){returnvnode(undefined,undefined,undefined,undefined,child);}returnchild;}))}//生成vnode节点函数vnode(type,key,props={},children,text,domElement){return{type,key,props,children,text,domElement}}3.渲染节点1.把虚拟DOM节点封装成一个真实的DOM节点functioncreateDOMElementFromVnode(vnode){lettype=vnode.type;让孩子们=vnode.children;if(type){//普通节点eg:div,spanvnode.domElement=document.createElement(vnode.type);更新属性(vnode);if(Array.isArray(children)){children.map(child=>{returnvnode.domElement.appendChild(createDOMElementFromVnode(child))})}}else{//文本节点vnode.domElement=document.createTextNode(vnode.text);}returnvnode.domElement;}2.更新属性functionupdateProperties(vnode,oldProps={}){letnewProps=vnode.props;让domElement=vnode.domElement;让oldStyle=oldProps.style||{};让newStyle=newProps.style||{};//遍历旧属性(样式和属性),看是否有新属性for(letiteminoldProps){if(!newProps[item]){deletedomElement[item];}}for(letiteminoldStyle){if(!newStyle[item]){domElement.style[item]="";}}//遍历新属性style并单独赋值for(letiteminnewProps){if(item=='style'){letstyleObj=newProps[item];for(letstyleIteminstyleObj){domElement.style[styleItem]=styleObj[styleItem];}}else{domElement[item]=newP绳索[项目];}}}3。Rendernodefunctionmount(vnode,root){root.appendChild(createDOMElementFromVnode(vnode));}4.patchupdate1.更新新旧节点//更新新旧节点函数patch(oldVnode,newVnode){//如果节点不同,直接替换}//如果新节点是文本节点,则直接修改文本内容if(newVnode.text){returnoldVnode.domElement.textContent=newVnode.text;}让domElement=newVnode.domElement=oldVnode.domElement;updateProperties(newVnode,oldVnode.props);让oldChildren=oldVnode.children;让newChildren=newVnode.children;if(oldChildren.length>0&&newChildren.length>0){//新老节点都存在updateChildren(domElement,oldChildren,newChildren);}elseif(oldChildren.length>0){//老节点有子节点,新节点没有oldVnode.domElement.innerHTML='';}elseif(newChildren.length>0){//旧节点没有子节点,新节点有for(leti=0;ioldEndIndex){letbeforeDOMElement=newChildren[newStartIndex+1]?newChildren[newStartIndex+1].domElement:null;for(leti=newStartIndex;i<=newEndIndex;i++){parentDOMElement.insertBefore(createDOMElementFromVnode(newChildren[i]),beforeDOMElement);}}//删除剩余的旧节点if(newStartIndex>newEndIndex){for(leti=oldStartIndex;i<=oldEndIndex;i++){parentDOMElement.removeChild(oldChildren[i].domElement)}}}//得到对应的key位置indexfunctioncreateKeyToIndexMap(children){letmap={};for(leti=0;i

最新推荐
猜你喜欢