【vue源码系列7】vue是如何创建虚拟dom,又是如何渲染成真实dom的?

189 阅读3分钟

学习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++)
}

image.png

当然是,除了性能因素之外,操作dom还有如下一些缺陷。

1.重绘和回流:DOM操作会引起浏览器的重绘和回流(也称为重排),浏览器需要重新计算元素的位置和大小等属性,这些计算和渲染过程都需要消耗CPU资源和时间。 2.读写分离:在进行DOM操作时,通常需要先读取元素的属性,然后再修改属性,这个读写操作涉及到两次操作,增加了访问时间和消耗。 3.频繁操作:当DOM操作被频繁执行时,会增加浏览器的负担,导致页面的卡顿和响应速度下降。 4.内存泄漏:DOM操作可能会引起内存泄漏,比如移除元素时未正确清理事件监听器等。

2.vue是如何创建虚拟dom的

在vue中,我们通过h函数进行虚拟dom的创建。

如果对这个函数不太了解,可以先看官方的文档对函数的介绍。

从源码中,可以看出,h函数只是做了一些参数的处理,实际上调用的是createVNode函数。这也是为什么看element-plus框架源码的时候,有很多组件是直接调用createVNode函数的。

image.png

在createVNode中,实际调用的是_createVNode函数。

在该函数中,主要做了3件事。

1.属性处理,主要是class跟style,因为这两个值,可以传字符串,还有传对象和数组。这里的处理就是把数组,跟对象拼成字符串。

2.标记虚拟dom类型

3.调用createBaseVNode函数生成虚拟dom。

image.png

接下去看createBaseVNode函数,看一下虚拟节点到底长啥样。

image.png

这个就是虚拟节点了,其中patchFlag是做标记的,shapeFlag是节点类型,type是元素类型。

声明完虚拟节点,就开始调用normalizeChildren函数递归标准化虚拟节点。

ps(代码中|跟&是位运算,值会转化成二进制进行计算) image.png

3.如何渲染虚拟dom

当我们创建了虚拟dom,我们会用render函数进行渲染。

render函数又会调用patch函数,所以我们直接看patch函数,虚拟节点的渲染与更新就都在这了。

image.png

而不管虚拟dom如何比对,最后必然是需要变成dom元素的。

我们可以先看一下processText这个函数。

image.png

代码很简单,n1为null就插入,否则就修改。

而修改dom调用的hostInsert是哪来的呢?

在创建render的时候,传进来的,也就是说我们操作dom的方法,其实是通过函数的参数传递进来的。

这也就是为什么vue的跨平台扩展性好的原因。

image.png

我们可以找一下,在runtime-dom下的nodeOps中,是不是找到了熟悉的dom操作。

image.png

总结一下

dom操作很消耗性能,使用虚拟节点可以大大的提高性能。

虚拟节点其实就是一段树形结构的js代码,里面包含了一些标记信息。

虚拟节点的渲染与更新是通过patch来进行的。

真实dom的创建,是通过参数,将dom操作的方法传入来实现的,这也是vue扩展性高的原因。

由于源码内容较多,可先查看这个迷你版本,项目实现了vue的核心逻辑,大大简化了阅读难度。


如果看完有收获,欢迎点赞、评论、分享支持。你的支持和肯定,是我写作的动力