前面的话
因疫情影响,小柒已经22天没碰过电脑,博客也没有更新。今天喜提充电器,无比激动!!! 小柒将继续更新博客,之前的Vue每日一题也会补起来。又可以与小伙伴们一起学习进步,开心!!!
什么是virtual dom?
virtual dom即虚拟dom,不是真实的dom。它是用js模拟
的DOM结构,是一个js对象
。
看一下真实dom与虚拟dom的区别:
真实dom:
<div id = "main">
<p>文本内容</p>
<p>文本内容</p>
</div>
虚拟dom:
{
tag: 'div',
attributes: {
id: 'main'
},
children: [
{
tag: 'p',
children: ['文本内容']
}
{
tag: 'p',
children: ['文本内容']
}
]
}
为何会存在virtual dom?
在了解什么是虚拟dom之后,我们要知道为什么vue 2.x会加入虚拟dom。
原因:使用虚拟dom来更新dom节点,来提高渲染性能
。
-
如果不使用虚拟dom,当上面的dom节点变成下面的情况时:
<div id = "main"> <p>哈哈哈哈</p> <p>文本内容</p> <p>hhhhhhh</p> </div>
浏览器会重新渲染整个
div
里面的节点,尽管第二个p
节点没有变化,也会重新渲染,然后浏览器最最消耗性能的就是操作DOM
。 -
如果使用虚拟dom, 将会对比DOM,只需要修改相应的部分就可以。对于上面的代码,浏览器只会重新渲染第一个
p
节点与第三个p
节点,而不会重新渲染div
中的所用节点。这个对比的过程都是基于js来计算的,计算在多次的js,也比不过一次dom操作消耗性能。
virtual dom 是如何应用的?核心API是什么?
首先 vue 的虚拟 DOM 的算法是基于snabbdom
这个库进行改造的。
snabbdom
中的核心api:h函数
与patch函数
。
-
h函数: 作用比较简单,根据传进来的参数来生成一个Vnode(虚拟节点)
-
patch函数: 作用通过vnode生成真实的dom。它有两个重要的函数:
1、patch(container,vnode) :初次渲染的时候,将VDOM渲染成真正的DOM然后插入到容器里面。
2、patch(vnode,newVnode):再次渲染的时候,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。
patch(container,vnode): 我们通过以下模拟代码,可以了解大致过程:
function createElement(vnode) {
var tag = vnode.tag
var attrs = vnode.attrs || {}
var children = vnode.children || []
if (!tag) {
return null
}
// 创建真实的 DOM 元素
var elem = document.createElement(tag)
// 属性
var attrName
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// 给 elem 添加属性
elem.setAttribute(attrName, attrs[attrName])
}
}
// 子元素
children.forEach(function (childVnode) {
// 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
elem.appendChild(createElement(childVnode)) // 递归
}) // 返回真实的 DOM 元素
return elem
}
patch(vnode,newVnode):这里我们只考虑vnode与newVnode如何对比的情况:
function updateChildren(vnode, newVnode) {
var children = vnode.children || []
var newChildren = newVnode.children || []
// 遍历现有的children
children.forEach(function (childVnode, index) {
var newChildVnode = newChildren[index]
// 两者tag一样
if (childVnode.tag === newChildVnode.tag) {
// 深层次对比,递归
updateChildren(childVnode, newChildVnode)
} else {
// 两者tag不一样
replaceNode(childVnode, newChildVnode)
}
}
)}
什么是diff算法?
diff算法并不是vue中新东西,其实在很多情况下我们都遇到,比如说git中的diff命令,就可以找出两个文件中不同之处。
而vue中的diff算法,其实就是用来找需要更新的节点的一个过程。vue中的diff算法是在同级比较,这里插一张网上到处都在使用的图:
仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。
diff 算法包括几个步骤:
- 用 JavaScript 对象结构表示 DOM 树的结构(即虚拟dom);然后用这个树构建一个真正的 DOM 树,插到文档当中。即上述提到的 patch(container,vnode)函数。
- 当状态变更的时候,重新构造一棵新的对象树(即新的虚拟dom)。然后用新的树和旧的树进行比较,记录两棵树差异。把所记录的差异应用到所构建的真正的DOM树上,视图就更新了 。即patch(vnode,newVnode)函数。
也就是说,diff的过程就是调用patch函数,就像打补丁一样修改真实dom。
最后
Vue中是如何生成虚拟dom的,那就是render函数
。渲染函数就是用来生成虚拟DOM的。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 虚拟节点(Vnode)。
Vue中使用template
模板来构建应用界面,在beforeMount钩子函数
之前,我们就会将模板编译成render函数,执行render函数返回虚拟DOM树。在改变DOM时,beforeUpdata钩子函数
之前,就会使用diff算法来找出两份虚拟DOM之间的差异。beforeUpdata钩子函数
之后便会重新渲染更新DOM。达到减少对DOM的操作,从而减少浏览器的开销,提高渲染速度,改善用户体验。(还是借用网上的图片)