Vue使用虚拟dom进行渲染view的方法


Posted in Javascript onDecember 26, 2019

前提

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);
};

new Watcher(vm, updateComponent, noop, {
 before: function before () {
 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方法的返回值

var patch = 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)) {
 // empty mount (likely as component), create new root element
 isInitialPatch = true;
 createElm(vnode, insertedVnodeQueue);
}

如果以上不成立,则新的vnode和oldVnode都存在.如果oldVnode不是真实的dom,则为虚拟dom节点,并且新老vnode相似,则进行diff算法

var isRealElement = isDef(oldVnode.nodeType);
if (!isRealElement && sameVnode(oldVnode, vnode)) {
 // patch existing root node
 patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly);
}

如果新老vnode不同,则拿到oldVnode的父节点,辅助创建vnode的新节点

var oldElm = oldVnode.elm;
var parentElm = nodeOps.parentNode(oldElm);

// create new node
createElm(
 vnode,
 insertedVnodeQueue,
 // extremely rare edge case: do not insert if old element is in a
 // leaving transition. Only happens when combining transition +
 // 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); }
} else if (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);
} else if (isDef(oldCh)) {
 // 如果oldVnode存在children,但是新的没有,则删除oldVnode的children的vnode
 removeVnodes(elm, oldCh, 0, oldCh.length - 1);
} else if (isDef(oldVnode.text)) {
 // 如果oldVnode有文本信息,则将dom的文本清空
 nodeOps.setTextContent(elm, '');
}

如果vnode是文本节点, 则当新老节点文本不同,将dom的文本更新成新vnode的文本

else if (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的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
滚动条变色 隐藏滚动条与双击网页自动滚屏显示代码
Dec 28 Javascript
getElementsByTagName vs selectNodes效率 及兼容的selectNodes实现
Feb 26 Javascript
jquery数组之存放checkbox全选值示例代码
Dec 20 Javascript
jQuery插件jcrop+Fileapi完美实现图片上传+裁剪+预览的代码分享
Apr 22 Javascript
javascript委托(Delegate)blur和focus用法实例分析
May 26 Javascript
JavaScript数组对象赋值用法实例
Aug 04 Javascript
基于jquery实现放大镜效果
Aug 17 Javascript
Javascript实现登录记住用户名和密码功能
Mar 22 Javascript
深入浅析Vue全局组件与局部组件的区别
Jun 15 Javascript
Layui表格监听行单双击事件讲解
Nov 14 Javascript
jquery.validate自定义验证用法实例分析【成功提示与择要提示】
Jun 06 jQuery
Openlayers实现距离面积测量
Sep 28 Javascript
node.js Promise对象的使用方法实例分析
Dec 26 #Javascript
js回调函数仿360开机
Dec 26 #Javascript
js仿360开机效果
Dec 26 #Javascript
element-ui中按需引入的实现
Dec 25 #Javascript
原生JS实现顶部导航栏显示按钮+搜索框功能
Dec 25 #Javascript
jQuery+PHP+Ajax实现动态数字统计展示功能
Dec 25 #jQuery
解决Vue 刷新页面导航显示高亮位置不对问题
Dec 25 #Javascript
You might like
探讨file_get_contents与curl效率及稳定性的分析
2013/06/06 PHP
php导入模块文件分享
2015/03/17 PHP
windows下的WAMP环境搭建图文教程(推荐)
2017/07/27 PHP
Thinkphp3.2简单解决多文件上传只上传一张的问题
2017/09/26 PHP
laravel5环境隐藏index.php后缀(apache)的方法
2019/10/12 PHP
JQuery this 和 $(this) 的区别
2009/08/23 Javascript
图片在浏览器中底部对齐 解决方法之一
2011/11/30 Javascript
JS二维数组的定义说明
2014/03/03 Javascript
js实现网页标题栏闪烁提示效果实例分析
2014/11/20 Javascript
CSS图片响应式 垂直水平居中
2015/08/14 Javascript
javascript下拉列表菜单的实现方法
2015/11/18 Javascript
动态JavaScript所造成一些你不知道的危害
2016/09/25 Javascript
微信js-sdk分享功能接口常用逻辑封装示例
2016/10/13 Javascript
深入了解JavaScript的逻辑运算符(与、或)
2016/12/20 Javascript
ajax 提交数据到后台jsp页面及页面跳转问题
2017/01/19 Javascript
使用Dropzone.js上传的示例代码
2017/10/10 Javascript
JS简单生成由字母数字组合随机字符串示例
2018/05/25 Javascript
JavaScript ES2019中的8个新特性详解
2019/02/20 Javascript
videocapture库制作python视频高速传输程序
2013/12/23 Python
Python列表(list)常用操作方法小结
2015/02/02 Python
Python最基本的输入输出详解
2015/04/25 Python
Python实现删除当前目录下除当前脚本以外的文件和文件夹实例
2015/07/27 Python
python开发之IDEL(Python GUI)的使用方法图文详解
2015/11/12 Python
PyQt5每天必学之拖放事件
2020/08/27 Python
Python缓存技术实现过程详解
2019/09/25 Python
Python多线程实现支付模拟请求过程解析
2020/04/21 Python
Django中的模型类设计及展示示例详解
2020/05/29 Python
纯css3制作的火影忍者写轮眼开眼至轮回眼及进化过程实例
2014/11/11 HTML / CSS
科室工作个人总结的自我评价
2013/10/29 职场文书
生物科学系大学生的自我评价
2013/12/20 职场文书
运动会解说词50字
2014/01/18 职场文书
高三政治教学反思
2014/02/06 职场文书
计算机网络及管理学专业求职信
2014/06/05 职场文书
机关职员工作检讨书
2014/10/23 职场文书
给校长的建议书作文500字
2015/09/14 职场文书
我国拿下天问一号火星着陆区附近 22 个地理实体命名:平乐、西柏坡、古田、漠河等
2022/04/29 数码科技