Vue使用虚拟dom进行渲染view的方法
前提
vue版本:v2.5.17-beta.0
触发render
vue在数据更新后会自动触发view的render工作,其依赖于数据驱动;在数据驱动的工作下,每一个vue的data属性都被监听,并且在set触发时,派发事件,通知收集到的依赖,从而触发对应的操作,render工作就是其中的一个依赖,并且被每一个data属性所收集,因此每一个data属性改变后,都会触发render。
vue更新监听
看一段代码
//来自mountComponent函数 updateComponent=function(){ vm._update(vm._render(),hydrating); }; newWatcher(vm,updateComponent,noop,{ before:functionbefore(){ if(vm._isMounted){ callHook(vm,'beforeUpdate'); } } },true/*isRenderWatcher*/);
updateComponent是更新组件的函数,内部调用vm._update,并且传参vm._render();
- vm._render()返回了什么?看源码则得知返回了虚拟dom(VNode)
- vm._update函数又做了什么事情?顾名思义,更新传入的vnode
- 什么时候触发updateComponent函数?在任何vue的data属性更改值都会触发
更新view
阅读_update函数得知,最终调用了vm.__patch__方法,最终找到为createPatchFunction方法的返回值
varpatch=createPatchFunction({nodeOps:nodeOps,modules:modules}); Vue.prototype.__patch__=inBrowser?patch:noop;
接下来重点看createPatchFunction的返回函数patch.
如果新的vnode不存在,则注销旧的vnode
if(isUndef(vnode)){ if(isDef(oldVnode)){invokeDestroyHook(oldVnode);} return }
如果旧的vnode不存在,则创建新的vnode
if(isUndef(oldVnode)){ //emptymount(likelyascomponent),createnewrootelement isInitialPatch=true; createElm(vnode,insertedVnodeQueue); }
如果以上不成立,则新的vnode和oldVnode都存在.如果oldVnode不是真实的dom,则为虚拟dom节点,并且新老vnode相似,则进行diff算法
varisRealElement=isDef(oldVnode.nodeType); if(!isRealElement&&sameVnode(oldVnode,vnode)){ //patchexistingrootnode patchVnode(oldVnode,vnode,insertedVnodeQueue,removeOnly); }
如果新老vnode不同,则拿到oldVnode的父节点,辅助创建vnode的新节点
varoldElm=oldVnode.elm; varparentElm=nodeOps.parentNode(oldElm); //createnewnode createElm( vnode, insertedVnodeQueue, //extremelyrareedgecase:donotinsertifoldelementisina //leavingtransition.Onlyhappenswhencombiningtransition+ //keep-alive+HOCs.(#4590) oldElm._leaveCb?null:parentElm, nodeOps.nextSibling(oldElm) );
以上的步骤发现,更新view时,重点进入到了patchVnode函数,因此下面进入patchVnode的函数阅读
如果新老node相等,则不做处理
if(oldVnode===vnode){ return }
如果vnode不是文本节点则有几种情况
if(isDef(oldCh)&&isDef(ch)){ //如果oldVnode和vnode的children都有值(组件层),并且不想等,则执行更新children流程 if(oldCh!==ch){updateChildren(elm,oldCh,ch,insertedVnodeQueue,removeOnly);} }elseif(isDef(ch)){ //如果vnode的children有值,如果当前dom有文本则清空, //并将oldVnode的dom作为父节点,创建新vnode的children节点 if(isDef(oldVnode.text)){nodeOps.setTextContent(elm,'');} addVnodes(elm,null,ch,0,ch.length-1,insertedVnodeQueue); }elseif(isDef(oldCh)){ //如果oldVnode存在children,但是新的没有,则删除oldVnode的children的vnode removeVnodes(elm,oldCh,0,oldCh.length-1); }elseif(isDef(oldVnode.text)){ //如果oldVnode有文本信息,则将dom的文本清空 nodeOps.setTextContent(elm,''); }
如果vnode是文本节点,则当新老节点文本不同,将dom的文本更新成新vnode的文本
elseif(oldVnode.text!==vnode.text){ nodeOps.setTextContent(elm,vnode.text); }
patchVnode函数处理的情况梳理一下则为:
- 如果新老vnode相同,不作处理
- 如果新vnode是文本节点,并且新老文本不同,将dom更新为vnode的文本
- 如果新老vnode都有children,表示他们是组件层,则调用updateChildren去做组件层更新
- 如果新vnode是组件层,oldVnode不是,则将当前dom添加新vnode组件的子元素
- 如果oldVnode是组件层,新vnode不是,则操作dom,将oldVnode包含的子元素删除
- 如果新vnode是组件层,oldVnode是文本节点,则将dom的文本清空
在patchVnode部分又浮现了一个新的函数:updateChildren,是在新老vnode都不是文本节点时调用的,这里就是vue的渲染机制的核心
updateChildren
updateChildren中将新老vnode的children进行的循环处理,每一次循环去判断是否有相同的vnode,如果没有则查找当前新vnode的子节点的key是否存在oldVnode的children中,如果不存在在这存在但已经不相同则创建新的dom,否则,如果是新老节点相同,回调patchVnode函数去处理2个节点。这样进行了递归处理,组件层的更新就完成了。
结尾
本文为看源码分析vue更新机制部分,省略了特殊场景的源码分析,比如ssr、静态组件等。
总结
以上所述是小编给大家介绍的Vue使用虚拟dom进行渲染view的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。