Vue如何用虚拟dom进行渲染view的
前提
触发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,如果不存在或存在但已经不相同则创建新的dom,否则,如果是新老节点相同,回调patchVnode函数去处理2个节点。 这样进行了递归处理,组件层的更新就完成了。
结尾
本文为看源码分析vue更新机制部分,省略了特殊场景的源码分析,比如ssr、静态组件等。
Vue如何用虚拟dom进行渲染view的的更多相关文章
- Vue源码探究-虚拟DOM的渲染
Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...
- vue中的虚拟DOM树
什么是虚拟DOM树?(Virtual DOM) 虚拟DOM树其实就是一个普通的js对象,它是用来描述一段HTML片段的 01 当页面渲染的时候Vue会创建一颗虚拟DOM树 02 ...
- 详解Vue中的虚拟DOM
摘要: 什么是虚拟DOM? 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并 ...
- vue核心之虚拟DOM
一.前言 虚拟DOM概念随着react的诞生而诞生,由facebook提出,其卓越的性能很快得到广大开发者的认可:继react之后vue2.0也在其核心引入了虚拟DOM的概念,本文将以vue2.0使用 ...
- react通过自己的jsx语法将两者放在一起通过虚拟dom来渲染
目前较为流行的react确实有很多优点,例如虚拟dom,单向数据流状态机的思想.还有可复用组件化的思想等等.加上搭配jsx语法和es6,适应之后开发确实快捷很多,值得大家去一试.其实组件化的思想一直在 ...
- vue 虚拟dom
https://segmentfault.com/a/1190000008291645 一个VNode的实例对象包含了以下属性 tag: 当前节点的标签名 data: 当前节点的数据对象 VNode可 ...
- vue2.0的虚拟DOM渲染
1.为什么需要虚拟DOM 前面我们从零开始写了一个简单的类Vue框架(文章链接),其中的模板解析和渲染是通过Compile函数来完成的,采用了文档碎片代替了直接对页面中DOM元素的操作,在完成数据的更 ...
- vue 的虚拟 DOM 有什么好处?
vue 中的虚拟DOM有什么好处?快! 首先了解浏览器显示网页经历的5个过程 1.解析标签,生成元素树(DOM树) 2.解析样式,生成样式树 3.生成元素与样式的关系 4.生成元素的显示坐标 5.显示 ...
- vue核心---虚拟dom的实现
生成dom的过程 由vue模板生成虚拟dom 虚拟dom转换成真实dom渲染到html页面 代码实现 要实现的真实dom <div id="box"> <p cl ...
随机推荐
- JS高级---闭包小案例
闭包小案例 普通的函数 //普通的函数 function f1() { var num = 10; num++; return num; } console.log(f1()); //11 conso ...
- 部署web应用程序到tomcat
昨天将一个web项目部署到本地的tomcat,历程很艰辛,各种报错.首先这个项目可以用eclipse内嵌的jetty启动起来,试着用tomcat容器,各种报错.以下是详细步骤: 1.用eclipse打 ...
- spring(三):BeanFactory
- koa2第一天 async详解
一.什么是async async其实是ES7的才有的关键字,放在这里说,其实是和我们前面所说的Promise,Generator有很大关联的.async的意思是"异步",顾名 ...
- NOIP做题练习(day1)
A - Xenny and Alternating Tasks 题面 题解 枚举第一天是谁做,将两个答案取\(min\)即可. 代码 #include <iostream> #includ ...
- YAML(YML)语法详解
ansible playbook是由yaml(yml)语法书写,结构清晰,可读性强,所以必须掌握yaml(yml)基础语法 语法 描述 锁进 YAML使用固定的缩进风格表示层级结构,每个缩进由两个空 ...
- vue 使用 jsonp 请求数据
vue 使用 jsonp 请求数据 vue请求数据的时候,会遇到跨域问题,服务器为了保证信息的安全,对跨域请求进行拦截,因此,为了解决vue跨域请求问题,需要使用jsonp. 安装jsonp npm ...
- MySQL移动数据库位置
http://zlyang.blog.51cto.com/1196234/1726029 需求:MySQL数据库文件原位置:/var/lib/mysql 要移动至:/data/mysql 1.首先在/ ...
- (转)多进程 & 多线程的区别与适用场景
转自:http://www.cnblogs.com/huntfor/p/4021327.html 关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”, ...
- multiprocessing 多进程实现 生产者与消费者模型JoinableQueue
from multiprocessing import JoinableQueue import time import random import asyncio import logging fr ...