最近复习到虚拟DOM与Diff,翻阅了众多资料,特此总结了这篇长文,加深自己对vue的理解。这篇文章比较详细的分析了vue的虚拟DOM,Diff算法,其中一些关键的地方从别处搬运了一些图进行说明(感谢制图的大佬),也包含比较详细的源码解读。
在讲虚拟DOM之前,先说一下真实DOM的渲染。
浏览器真实DOM渲染的过程大概分为以下几个部分
上面这是一次DOM渲染的过程,如果dom更新,那么dom需要重新渲染一次,如果存在下面这种情况
<body>
<div id="container">
<div class="content" style="color: red;font-size:16px;">
This is a container
</div>
....
<div class="content" style="color: red;font-size:16px;">
This is a container
</div>
</div>
</body>
<script>
let content = document.getElementsByClassName('content');
for (let i = 0; i < 1000000; i++) {
content[i].innerHTML = `This is a content${i}`;
// 触发回流
content[i].style.fontSize = `20px`;
}
</script>
那么需要真实的操作DOM100w次,触发了回流100w次。每次DOM的更新都会按照流程进行无差别的真实dom的更新。所以造成了很大的性能浪费。如果循环里面是复杂的操作,频繁触发回流与重绘,那么就很容易就影响性能,造成卡顿。另外这里要说明一下的是,虚拟DOM并不是意味着比DOM就更快,性能需要分场景,虚拟DOM的性能跟模板大小是正相关。虚拟DOM的比较过程是不会区分数据量大小的,在组件内部只有少量动态节点时,虚拟DOM依然是会对整个vdom进行遍历,相比直接渲染而言是多了一层操作的。
<div class="list">
<p class="item">item</p>
<p class="item">item</p>
<p class="item">item</p>
<p class="item">{{ item }}</p>
<p class="item">item</p>
<p class="item">item</p>
</div>
比如上面这个例子,虚拟DOM。虽然只有一个动态节点,但是虚拟DOM依然需要遍历diff整个list的class,文本,标签等信息,最后依然需要进行DOM渲染。如果只是dom操作,就只要操作一个具体的DOM然后进行渲染。虚拟DOM最核心的价值在于,它能通过js描述真实DOM,表达力更强,通过声明式的语言操作,为开发者提供了更加方便快捷开发体验,而且在没有手动优化,大部分情景下,保证了性能下限,性价比更高。
虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构。tag用来描述标签,props用来描述属性,children用来表示嵌套的层级关系。
const vnode = {
tag: 'div',
props: {
id: 'container',
},
children: [{
tag: 'div',
props: {
class: 'content',
},
text: 'This is a container'
}]
}
//对应的真实DOM结构
<div id="container">
<div class="content">
This is a container
</div>
</div>