在程序员的职业生涯中,无论是在跳槽时还是晋升时都会遇到各式各样的面试,那么就技术层面上而言,这篇长沙IT培训程序员面试题:DOM DIFF希望能给你帮助。
通过JS层面的计算(逐层比对虚拟DOM对象),生成patch对象(即补丁对象),然后对补丁进行解析和重新渲染
对树进行分层比较,两棵树只会对同一层次的节点进行比较(不会跨节点比较)
React 只会简单的考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作
可以处理同级节点顺序的改变每一层级进行对比的计算法则:深度优先遍历节点类型相同,看属性是否相同,如果不同,则产生属性的补丁包 {type:’ATTRS’,attrs:{class:’BB’}}新的DOM节点不存在,被删除了{type:’REMOVE’,index:xxx}节点类型不相同,直接替换即可 {type:’REPLACE’,newNode:xxx}文本的内容进行变化 {type:’TEXT’,text:xxx}…
function diff(oldTree,newTree){ let patchs = {}; //=>补丁包 按照层级放置补丁包 {1:[],2:[]} let index = 0; //=>比较的层级 //=>递归树,比较后的结果放到补丁包 walk(oldTree,newTree,index,patchs); return patchs;}function walk(oldNode,newNode,index,patchs){ let currentPatch={}; if(!newNode){ //=>新元素不存在,代表删除 currentPatch.push({ type:'EMOVE', index }); }else if(typeof oldNode==="string"&&typeof newNode==="string"){ //=>如果是文本,判断文本是否改变 if(oldNode!==newNode){ currentPatch.push({ type:'TEXT', text:newNode }); } }else if(oldNode.type===newNode.type){ //=>比较属性是否有改 let attrs=diffAttr(oldNode.props,newNode.props);, if(Object.keys(attrs).length>0){ currentPatch.push({ type:'ATTRS', attrs }); } //=>如果有儿子节点,则遍历儿子 diffChildren( oldNode.props.childrren, newNode.props.childrren, index,patchs); }else{ //=>节点被替换了 currentPatch.push({ type:'REPACE', newNode }); } //=>当前本级查找,确实有补丁,我们放到外层补丁包中 if(currentPatch.length>0){ patchs[index]=currentPatch; }}//=>比较儿子function diffChildren(oldChildren,newChildren,index,patchs){ oldChildrren.forEach((child,ind)=>{ walk(child,newChildren[ind],++index,patchs); })} //=>比较属性,生成补丁包function diffAttr(oldAttrs,newAttrs){ let patch={}; for(ley key in oldAttrs){ //=>属性不一样(可能是把老的中某个删除了,这样获取的结果可能是undefined) if(oldAttrs[key]!==newAttrs[key]){ patch[key]=newAttrs[key]; } } for(ley key in newAttrs){ //=>看老的节点中是否有这样一个属性(没有就是新增) if(!oldAttrs.hasOwnProperty(key)){ patch[key]=newAttrs[key]; } } return patch;}复制代码根据补丁重新渲染let patchs=diff(xxx,xxx); //=>两个虚拟DOMlet node;let index=0;walk(node);functon walk(node){ let currentPatch=patches[index++]; let cildNodes=node.childNodes; cildNodes.forEach(child=>walk(child)); if(currentPatch.length>0){ doPatch(node,currentPatch); }}function doPatch(node,patch){ patchs.forRach((item,index)=>{ switch(patch.type){ case 'ATTRS': for(let key in patch.attrs){ let val=patch.attrs[key]; if(val){ setAttr(node,key,val); }else{ node.removeAttribute(key) } } break; case 'TEXT': node.textContent=patch.text; break; case 'REPLACE': let newNode=(patch.newNode instanceof Element)?render(patch.newNode):document.createTextNode(patch.newNode); node.parentNode.replaceChild(newNode,node); break; case 'REMOVE': node.parentNode.removeChild(node); break; } }) }复制代码总结通过JS层面计算 === 对比的是虚拟DOM对象次加载页面,所有的内容都要重新渲染(语法解析 -> 虚拟DOM -> 真实DOM -> 浏览器渲染) =>次加载页面越少渲染越好上一次计算出来的虚拟DOM对象会存储起来,当状态或者其它数据改变,重新渲染组件(重新生成一套虚拟DOM对象)把重新生成的虚拟DOM 和 之前存储起来的虚拟 DOM进行对比=>把不一样的以补丁的形式存储起来(存储的还是对象)重新渲染的过程,只是把补丁渲染到页面中,原有渲染过但是没有改变的东西是不需要处理的
相关文章
06.29抢座
了解千锋动态
关注千锋教育服务号
扫一扫快速进入
千锋移动端页面
扫码匿名提建议
直达CEO信箱