虚拟 DOM 篇
学习什么是虚拟 DOM,以及Vue中的DOM-Diff原理虚拟DOM简介
1、什么是虚拟DOM
虚拟DOM,就是用一个JS对象来描述一个DOM节点。如:
<div class="example" id="content">
aaaa
</div>
var domVer = {
tag:'div',
attrs:{
class:'example',
id:'content'
},
text:'aaaa',
children:[] //子元素
}
//组成一个DOM节点必要的东西通过一个JS对象表现出来,
那么这个JS对象就可以用来描述这个DOM节点,
我们把这个JS对象就称为是这个真实DOM节点的虚拟DOM节点。
2、Vue中的虚拟DOM
Vue中存在一个VNode类,通过这个类,我们可以实例化出不同类型的虚拟DOM节点。具体看源码:src/core/vdom/vnode.js
VNode的作用:在视图渲染之前,template模板先编译成VNode并缓存下来,等到数据发生变化页面需要重新渲染的时候,把数据发生变化后生成的VNode与前一次缓存下来的VNode进行对比找出差异。然后有差异的VNode对应真实的DOM的节点就是需要重新渲染的节点,最后根据有差异的DOM节点创建出真实的DOM节点在插入到视图中,完成视图更新。
VNode可以描述六种类型的节点:
(1)注释节点
(2)文本节点
(3)克隆节点
(4)元素节点
(5)组件节点
(6)函数式组件节点
3、DOM-Diff
对比新旧两份VNode并找出差异的过程就是所谓的DOM-Diff过程。在Vue中,把 DOM-Diff过程叫做patch过程。以新的VNode为基准,改造旧的oldVNode使之成为跟新的VNode一样,这就是patch过程要干的事。具体看源码:/src/core/vdom/patch.js
(1)创建节点
只有三种节点能被创建并插入到DOM中,(元素节点、文本节点、注释节点)。所以vue在创建节点之前会先判断需要创建的节点是什么类型的。
(2)删除节点
function removeNode (el) {
const parent = nodeOps.parentNode(el) // 获取父节点
if (isDef(parent)) {
nodeOps.removeChild(parent, el) // 调用父节点的removeChild方法
}
}
(3)更新节点
更新节点的时候我们需要对以下3种情况进行判断并分别处理:
a、如果VNode和oldVNode均为静态节点
我们说了,静态节点无论数据发生任何变化都与它无关,所以都为静态节点的话则直接跳过,无需处理。
b、如果VNode是文本节点
如果VNode是文本节点即表示这个节点内只包含纯文本,那么只需看oldVNode是否也是文本节点,如果是,那就比较两个文本是否不同,如果不同则把oldVNode里的文本改成跟VNode的文本一样。如果oldVNode不是文本节点,那么不论它是什么,直接调用setTextNode方法把它改成文本节点,并且文本内容跟VNode相同。
c、如果VNode是元素节点
如果VNode是元素节点,则又细分以下两种情况:
该节点包含子节点
如果新的节点内包含了子节点,那么此时要看旧的节点是否包含子节点,
如果旧的节点里也包含了子节点,那就需要递归对比更新子节点;
如果旧的节点里不包含子节点,那么这个旧节点有可能是空节点或者是文本节点,
如果旧的节点是空节点就把新的节点里的子节点创建一份然后插入到旧的节点里面,
如果旧的节点是文本节点,则把文本清空,然后把新的节点里的子节点创建一份然后插入到旧的节点里面。
该节点不包含子节点
如果该节点不包含子节点,同时它又不是文本节点,那就说明该节点是个空节点,那就好办了
,不管旧节点之前里面都有啥,直接清空即可。