学习vue源码,虚拟dom肯定是绕不过去的,所以今天聊一聊vue3源码中的虚拟dom。
虚拟dom其实本质上就是一段用来表示真实dom的一段js代码。
他最大的优点,是为了解决频繁的dom操作问题。
那么dom操作有哪些问题呢?
1.为什么需要虚拟dom
我们都知道dom操作是比较消耗性能的。
我们可以简单看一下,一个空的div元素有多少属性。
let a = document.createElement('div')
let i = 0
for (let name in a) {
console.log(name, i++)
}
当然是,除了性能因素之外,操作dom还有如下一些缺陷。
1.重绘和回流:DOM操作会引起浏览器的重绘和回流(也称为重排),浏览器需要重新计算元素的位置和大小等属性,这些计算和渲染过程都需要消耗CPU资源和时间。 2.读写分离:在进行DOM操作时,通常需要先读取元素的属性,然后再修改属性,这个读写操作涉及到两次操作,增加了访问时间和消耗。 3.频繁操作:当DOM操作被频繁执行时,会增加浏览器的负担,导致页面的卡顿和响应速度下降。 4.内存泄漏:DOM操作可能会引起内存泄漏,比如移除元素时未正确清理事件监听器等。
2.vue是如何创建虚拟dom的
在vue中,我们通过h函数进行虚拟dom的创建。
如果对这个函数不太了解,可以先看官方的文档对函数的介绍。
从源码中,可以看出,h函数只是做了一些参数的处理,实际上调用的是createVNode函数。这也是为什么看element-plus框架源码的时候,有很多组件是直接调用createVNode函数的。
在createVNode中,实际调用的是_createVNode函数。
在该函数中,主要做了3件事。
1.属性处理,主要是class跟style,因为这两个值,可以传字符串,还有传对象和数组。这里的处理就是把数组,跟对象拼成字符串。
2.标记虚拟dom类型
3.调用createBaseVNode函数生成虚拟dom。
接下去看createBaseVNode函数,看一下虚拟节点到底长啥样。
这个就是虚拟节点了,其中patchFlag是做标记的,shapeFlag是节点类型,type是元素类型。
声明完虚拟节点,就开始调用normalizeChildren函数递归标准化虚拟节点。
ps(代码中|跟&是位运算,值会转化成二进制进行计算)
3.如何渲染虚拟dom
当我们创建了虚拟dom,我们会用render函数进行渲染。
render函数又会调用patch函数,所以我们直接看patch函数,虚拟节点的渲染与更新就都在这了。
而不管虚拟dom如何比对,最后必然是需要变成dom元素的。
我们可以先看一下processText这个函数。
代码很简单,n1为null就插入,否则就修改。
而修改dom调用的hostInsert是哪来的呢?
在创建render的时候,传进来的,也就是说我们操作dom的方法,其实是通过函数的参数传递进来的。
这也就是为什么vue的跨平台扩展性好的原因。
我们可以找一下,在runtime-dom下的nodeOps中,是不是找到了熟悉的dom操作。
总结一下
dom操作很消耗性能,使用虚拟节点可以大大的提高性能。
虚拟节点其实就是一段树形结构的js代码,里面包含了一些标记信息。
虚拟节点的渲染与更新是通过patch来进行的。
真实dom的创建,是通过参数,将dom操作的方法传入来实现的,这也是vue扩展性高的原因。
由于源码内容较多,可先查看这个迷你版本,项目实现了vue的核心逻辑,大大简化了阅读难度。
如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力。