前言中经常看到Vue2的virtualDOMdiff原理的讲解,但是很多都是在原代码的基础上加了一些注释等等,这里先介绍一个Vue2的virtualDOM实现VNodesrc/core/vdom/Vnode。jsexport是从0行代码开始实现的标签;this.children=children这个.text=文本;this.elm=榆树;}}exportfunctioncreateTextNode(val){//为什么elm默认设置为undefined,而不是使用document.createElement(tagName)直接根据tag给elm赋值?但是稍后在创建createElm时分配值呢?返回新的VNode(undefined,undefined,String(val),undefined)}exportfunctioncreateCommentNode(tag,children){if(children){for(vari=0;iitem1item2item3先实现Vnode无diff渲染到页面中。为什么先实现无diff渲染Vnode的部分呢?打开,虚拟DOM的性能并不总是比非虚拟DOM快。首先,实现效用函数。不熟悉的可以手动敲代码熟悉一下。//真正的DOM操作src/core/vdom/node-ops.jsexportfunctioncreateElement(tagName){returndocument.createElement(tagName)}exportfunctioncreateTextNode(text){returndocument.createTextNode(text)}exportfunctioncreateComment(text){returndocument.createComment(text)}exportfunctioninsertBefore(parentNode)},newNode,referenceNode){parentNode.insertBefore(newNode,referenceNode)}导出函数removeChild(node,child){node.removeChild(child)}导出函数appendChild(node,child){node.appendChild(child)}exportfunctionparentNode(node){returnnode.parentNode}exportfunctionnextSibling(node){returnnode.nextSibling}exportfunctiontagName(node){returnnode.tagName}exportfunctionsetTextContent(node,text){node.textContent复制代码=text}exportfunctionsetAttribute(node,key,val){node.setAttribute(key,val)}src/main.jsimport{VNode,createCommentNode}from'./core/vdom/vnode'从'./core导入补丁/vdom/patch'varcontainer=document.getElementById("app");varoldVnode=newVNode(container.tagName,[],undefined,container);varnewVonde=createCommentNode('ul',[createCommentNode('li',['item1']),createCommentNode('li',['item2']),createCommentNode('li',['item3'])])console.time('start');patch(oldVnode,新冯德);//冲洗页面console.timeEnd('start');这里我们要实现一个补丁方法,把Vnode冲洗到页面中src/core/vdom/patch.jsimport*asnodeOpsfrom'./node-ops'importVNodefrom'./vnode'导出默认函数patch(oldVnode,vnode){letisInitialPatch=false;if(sameVnode(oldVnode,vnode)){//如果两个Vnode节点的root一致,则启动diffpatchVnode(oldVnode,vnode)}else{//这是没有diff的实现constoldElm=oldVnode.elm;constparentElm=nodeOps.parentNode(oldElm);createElm(vnode,parentElm,nodeOps.nextSibling(oldElm))if(parentElm!=null){removeVnodes(parentElm,[oldVnode],0,0)}}returnvnode.elm;}函数patchVnode(oldVnode,vnode,removeOnly){if(oldVnode===vnode){return}constelm=vnode.elm=oldVnode.elmconstoldCh=oldVnode.children;constch=vnode.childrenif(isUndef(vnode.text)){//非文本节点if(isDef(oldCh)&&isDef(ch)){//都有字节节点if(oldCh!==ch){//更新孩子updateChildren(elm,oldCh,ch,removeOnly);}}elseif(isDef(ch)){//新的有子节点,旧的没有if(isDef(oldVnode.text)){nodeOps.setTextContent(elm,'');}//添加子节点addVnodes(elm,null,ch,0,ch.length-1)}elseif(isDef(oldCh)){//旧的有子节点,新的没有removeVnodes(elm,oldCh,0,oldCh.length-1)}elseif(isDef(oldVnode.text)){//否则旧的有文本内容,空着就行nodeOps.setTextContent(elm,'');}}elseif(oldVnode.text!==vnode.text){//直接修改文本nodeOps.setTextContent(elm,vnode.text);}}functionupdateChildren(parentElm,oldCh,newCh,removeOnly){//仔细看这里,没有难度,如果没有,也可以搜索下面描述这个过程的图文letoldStartIdx=0;让newStartIdx=0;让oldEndIdx=oldCh.length-1;让oldStartVnode=oldCh[0];让oldEndVnode=oldCh[oldEndIdx];让newEndIdx=newCh.length-1;letnewStartVnode=newCh[0]letnewEndVnode=newCh[newEndIdx]letrefElm;constcanMove=!removeOnlywhile(oldStartIdx<=oldEndIdx&&newStartIdx<=newEndIdx){if(isUndef(oldStartVnode)){oldStartVnode=oldCh[++oldStartIdx]}elseif(isUndef(oldEndVnode)){oldEndVnode=oldCh[--oldEndIdx]}elseif(sameVnode(oldStartVnode,newStartVnode)){patchVnode(oldStartVnode,newStartVnode)oldStartVnode=oldCh[++oldStartIdx]newStartVnode=newCh[++newStartIdx]}elseif(sameVnode(oldEndVnode,newEndVnode)){patchVnode(oldEndVnode,newEndVnode)oldEndVnode=oldCh[--oldEndIdx];newEndVnode=newCh[--newEndIdx];}elseif(sameVnode(oldStartVnode,newEndVnode)){patchVnode(oldStartVnode,newEndVnode);//更换顺序canMove&&nodeOps.insertBefore(parentElm,oldStartVnode.elm,nodeOps.nextSibling(oldEndVnode.elm))oldStartVnode=oldCh[++oldStartIdx]newEndVnode=newCh[--newEndIdx]}elseif(sameVnode(oldEndVnode,newStartVnode)))){页面atchVnode(oldEndVnode,newStartVnode)canMove&&nodeOps.insertBefore(parentElm,oldEndVnode.elm,oldStartVnode.elm)oldEndVnode=oldCh[--oldEndIdx]newStartVnode=newCh[++newStartIdx]}else{createElm(newStartVnode,parentElm,oldStartVnode.elm)newStartVnode=newCh[++newStartIdx];}}if(oldStartIdx>oldEndIdx){//旧的提前相遇,不比较添加新节点refElm=isUndef(newCh[newEndIdx+1])?null:newCh[newEndIdx+1].elmaddVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)}else{//新的早期遇到删除冗余节点removeVnodes(parentElm,oldCh,oldStartIdx,oldEndIdx)}}functionremoveVnodes(parentElm,vnodes,startIdx,endIdx){for(;startIdx<=endIdx;++startIdx){constch=vnodes[startIdx];if(isDef(ch)){removeNode(ch.elm)}}}functionaddVnodes(parentElm,refElm,vnodes,startIdx,endIdx){for(;startIdx<=endIdx;++startIdx){createElm(vnodes[startIdx],parentElm,refElm)}}函数sameVnode(vnode1,vnode2){returnvnode1.tag===vnode2.tag}functionremoveNode(el){constparent=nodeOps.parentNode(el)if(parent){nodeOps.removeChild(parent,el)}}函数removeVnodes(parentElm,vnodes,startIdx,endIdx){for(;startIdx<=endIdx;++startIdx){constch=vnodes[startIdx]if(isDef(ch)){removeNode(ch.elm)}}}functionisDef(s){returns!=null}functionisUndef(s){returns==null}functioncreateChildren(vnode,children){如果(Array.isArray(children)){for(leti=0;i