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 相关文章推荐
Extjs列表详细信息窗口新建后自动加载解决方法
Apr 02 Javascript
jquery ajax实现下拉框三级无刷新联动,且保存保持选中值状态
Oct 29 Javascript
jQuery实现简单网页遮罩层/弹出层效果兼容IE6、IE7
Jun 16 Javascript
Javascript学习笔记之 函数篇(二) : this 的工作机制
Jun 24 Javascript
JavaScript访问CSS属性的几种方式介绍
Jul 21 Javascript
基于JavaScript代码实现兼容各浏览器的设为首页和加入收藏
Jan 07 Javascript
JS代码实现百度地图 画圆 删除标注
Oct 12 Javascript
Angularjs中数据绑定的实例详解
Aug 25 Javascript
React-Native之定时器Timer的实现代码
Oct 04 Javascript
JS构造一个html文本内容成文件流形式发送到后台
Jul 31 Javascript
如何在JS文件中获取Vue组件
Sep 16 Javascript
JavaScript 与 TypeScript之间的联系
Nov 27 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
php数据库配置文件一般做法分享
2012/07/07 PHP
php猴子选大王问题解决方法
2015/05/12 PHP
PHP使用PHPexcel导入导出数据的方法
2015/11/14 PHP
php 指定范围内多个随机数代码实例
2016/07/18 PHP
php实现产品加入购物车功能(1)
2020/07/23 PHP
详解Yii2 之 生成 URL 的方法
2017/06/16 PHP
js下弹出窗口的变通
2007/04/18 Javascript
EASYUI TREEGRID异步加载数据实现方法
2012/08/22 Javascript
给artDialog 5.02 增加ajax get功能详细介绍
2012/11/13 Javascript
使用CSS和jQuery模拟select并附提交后取得数据的代码
2013/10/18 Javascript
鼠标移到div,浮层显示明细,弹出层与div的上边距左边距重合(示例代码)
2013/12/14 Javascript
jQuery动画出现连续触发、滞后反复执行的解决方法
2015/01/28 Javascript
js HTML5上传示例代码完整版
2016/10/10 Javascript
Easyui的组合框的取值与赋值
2016/10/28 Javascript
EditPlus中的正则表达式 实战(4)
2016/12/15 Javascript
vue cli 全面解析
2018/02/28 Javascript
webpack源码之loader机制详解
2018/04/06 Javascript
vue组件实现可搜索下拉框扩展
2020/10/23 Javascript
Vue组件之单向数据流的解决方法
2018/11/10 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
[01:51]开启你的城市传奇 完美世界城市挑战赛开始报名
2018/10/09 DOTA
Python实现的简单万年历例子分享
2014/04/25 Python
Python计算已经过去多少个周末的方法
2015/07/25 Python
Python3.5基础之NumPy模块的使用图文与实例详解
2019/04/24 Python
pyqt5 实现在别的窗口弹出进度条
2019/06/18 Python
Python pandas RFM模型应用实例详解
2019/11/20 Python
Python基于traceback模块获取异常信息
2020/07/23 Python
python爬取音频下载的示例代码
2020/10/19 Python
美国智能家居专家:tink
2019/06/04 全球购物
经济学博士求职自荐信范文
2013/11/23 职场文书
销售冠军获奖感言
2014/02/03 职场文书
省级青年文明号申报材料
2014/05/23 职场文书
自我查摆剖析材料
2014/10/11 职场文书
Nginx工作原理和优化总结。
2021/04/02 Servers
python实现简单的名片管理系统
2021/04/26 Python
代码复现python目标检测yolo3详解预测
2022/05/06 Python