Vue源码系列讲解——虚拟DOM篇【一】(Vue中的虚拟DOM)

目录

1. 前言

2. 虚拟DOM简介

 2.1什么是虚拟DOM?

 2.2为什么要有虚拟DOM?

3. Vue中的虚拟DOM

3.1 VNode类

3.2 VNode的类型

3.2.1 注释节点

3.2.2 文本节点

3.2.3 克隆节点

3.2.4 元素节点

3.2.5 组件节点

3.2.6 函数式组件节点

3.2.7 小结

3.3 VNode的作用

4. 总结

1. 前言

        虚拟DOM,这个名词作为当下的前端开发人员你一定不会陌生,至少会略有耳闻,但不会闻所未闻吧。这也是现在求职面试考察中非常高频的一个考点了。因为在当下的前端三大框架中关于虚拟DOM或多或少都有所涉及,那么接下来,我们就从源码角度出发,看看Vue中的虚拟DOM时怎样的。

2. 虚拟DOM简介

        由于本系列文章是针对Vue源码深入学习的,所以着重分析在Vue中对虚拟DOM是如何实现的,而对于虚拟DOM本身这个概念不做大篇幅的展开讨论,仅从以下几个问题简单介绍:

 2.1什么是虚拟DOM?

        所谓虚拟DOM,就是用一个JS对象来描述一个DOM节点,像如下示例:

<div class="a" id="b">我是内容</div>{tag:'div',        // 元素标签attrs:{           // 属性class:'a',id:'b'},text:'我是内容',  // 文本内容children:[]       // 子元素
}

我们把组成一个DOM节点的必要东西通过一个JS对象表示出来,那么这个JS对象就可以用来描述这个DOM节点,我们把这个JS对象就称为是这个真实DOM节点的虚拟DOM节点。

 2.2为什么要有虚拟DOM?

        我们知道,Vue是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM,而操作真实DOM又是非常耗费性能的,这是因为浏览器的标准就把 DOM 设计的非常复杂,所以一个真正的 DOM 元素是非常庞大的,如下所示:

let div = document.createElement('div')
let str = ''
for (const key in div) {str += key + ''
}
console.log(str)

上图中我们打印一个简单的空div标签,就打印出这么多东西,更不用说复杂的、深嵌套的DOM节点了。由此可见,直接操作真实DOM是非常消耗性能的。

        那么有没有什么解决方案呢?当然是有的。我们可以用JS的计算性能来换取操作DOM所消耗的性能。

        既然我们逃不掉操作DOM这道坎,但是我们可以尽可能少的操作DOM。那如何在更新视图的时候尽可能少的操作DOM呢?最直观的思路就是我们不要盲目的去更新视图,而是通过对比数据变化前后的状态,计算出视图中哪些地方需要更新,只更新需要更新的地方,而不需要更新的地方则不需关心,这样我们就可以尽可能少的操作DOM了。这也就是上面所说的用JS的计算性能来换取操作DOM的性能。

        我们可以用JS模拟出一个DOM节点,称之为虚拟DOM节点。当数据发生变化时,我们对比变化前后的虚拟DOM节点,通过DOM-Diff算法计算出需要更新的地方,然后去更新需要更新的视图。

        这就是虚拟DOM产生的原因以及最大的用途。

3. Vue中的虚拟DOM

       前文我们介绍了虚拟DOM的概念以及为什么要有虚拟DOM,那么在Vue中虚拟DOM是怎么实现的呢?接下来,我们从源码出发,深入学习一下。

3.1 VNode类

        我们说了,虚拟DOM就是用JS来描述一个真实的DOM节点。而在Vue中就存在了一个VNode类,通过这个类,我们就可以实例化出不同类型的虚拟DOM节点,源码如下:

// 源码位置:src/core/vdom/vnode.jsexport default class VNode {constructor (tag?: string,data?: VNodeData,children?: ?Array<VNode>,text?: string,elm?: Node,context?: Component,componentOptions?: VNodeComponentOptions,asyncFactory?: Function) {this.tag = tag                                /*当前节点的标签名*/this.data = data        /*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/this.children = children  /*当前节点的子节点,是一个数组*/this.text = text     /*当前节点的文本*/this.elm = elm       /*当前虚拟节点对应的真实dom节点*/this.ns = undefined            /*当前节点的名字空间*/this.context = context          /*当前组件节点对应的Vue实例*/this.fnContext = undefined       /*函数式组件对应的Vue实例*/this.fnOptions = undefinedthis.fnScopeId = undefinedthis.key = data && data.key           /*节点的key属性,被当作节点的标志,用以优化*/this.componentOptions = componentOptions   /*组件的option选项*/this.componentInstance = undefined       /*当前节点对应的组件的实例*/this.parent = undefined           /*当前节点的父节点*/this.raw = false         /*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/this.isStatic = false         /*静态节点标志*/this.isRootInsert = true      /*是否作为跟节点插入*/this.isComment = false             /*是否为注释节点*/this.isCloned = false           /*是否为克隆节点*/this.isOnce = false                /*是否有v-once指令*/this.asyncFactory = asyncFactorythis.asyncMeta = undefinedthis.isAsyncPlaceholder = false}get child (): Component | void {return this.componentInstance}
}

        从上面的代码中可以看出:VNode类中包含了描述一个真实DOM节点所需要的一系列属性,如tag表示节点的标签名,text表示节点中包含的文本,children表示该节点包含的子节点等。通过属性之间不同的搭配,就可以描述出各种类型的真实DOM节点。

3.2 VNode的类型

        上一小节最后我们说了,通过属性之间不同的搭配,VNode类可以描述出各种类型的真实DOM节点。那么它都可以描述出哪些类型的节点呢?通过阅读源码,可以发现通过不同属性的搭配,可以描述出以下几种类型的节点。

  • 注释节点
  • 文本节点
  • 元素节点
  • 组件节点
  • 函数式组件节点
  • 克隆节点

接下来,我们就把这几种类型的节点描述方式从源码中一一对应起来。

3.2.1 注释节点

        注释节点描述起来相对就非常简单了,它只需两个属性就够了,源码如下:

// 创建注释节点
export const createEmptyVNode = (text: string = '') => {const node = new VNode()node.text = textnode.isComment = truereturn node
}

        从上面代码中可以看到,描述一个注释节点只需两个属性,分别是:textisComment。其中text属性表示具体的注释信息,isComment是一个标志,用来标识一个节点是否是注释节点。

3.2.2 文本节点

        文本节点描述起来比注释节点更简单,因为它只需要一个属性,那就是text属性,用来表示具体的文本信息。源码如下:

// 创建文本节点
export function createTextVNode (val: string | number) {return new VNode(undefined, undefined, undefined, String(val))
}

3.2.3 克隆节点

        克隆节点就是把一个已经存在的节点复制一份出来,它主要是为了做模板编译优化时使用,这个后面我们会说到。关于克隆节点的描述,源码如下:

// 创建克隆节点
export function cloneVNode (vnode: VNode): VNode {const cloned = new VNode(vnode.tag,vnode.data,vnode.children,vnode.text,vnode.elm,vnode.context,vnode.componentOptions,vnode.asyncFactory)cloned.ns = vnode.nscloned.isStatic = vnode.isStaticcloned.key = vnode.keycloned.isComment = vnode.isCommentcloned.fnContext = vnode.fnContextcloned.fnOptions = vnode.fnOptionscloned.fnScopeId = vnode.fnScopeIdcloned.asyncMeta = vnode.asyncMetacloned.isCloned = truereturn cloned
}

        从上面代码中可以看到,克隆节点就是把已有节点的属性全部复制到新节点中,而现有节点和新克隆得到的节点之间唯一的不同就是克隆得到的节点isClonedtrue

3.2.4 元素节点

        相比之下,元素节点更贴近于我们通常看到的真实DOM节点,它有描述节点标签名词的tag属性,描述节点属性如classattributes等的data属性,有描述包含的子节点信息的children属性等。由于元素节点所包含的情况相比而言比较复杂,源码中没有像前三种节点一样直接写死(当然也不可能写死),那就举个简单例子说明一下:

// 真实DOM节点
<div id='a'><span>难凉热血</span></div>// VNode节点
{tag:'div',data:{},children:[{tag:'span',text:'难凉热血'}]
}

        我们可以看到,真实DOM节点中:div标签里面包含了一个span标签,而span标签里面有一段文本。反应到VNode节点上就如上所示:tag表示标签名,data表示标签的属性id等,children表示子节点数组。

3.2.5 组件节点

组件节点除了有元素节点具有的属性之外,它还有两个特有的属性:

  • componentOptions :组件的option选项,如组件的props
  • componentInstance :当前组件节点对应的Vue实例
3.2.6 函数式组件节点

函数式组件节点相较于组件节点,它又有两个特有的属性:

  • fnContext:函数式组件对应的Vue实例
  • fnOptions: 组件的option选项
3.2.7 小结

        以上就是VNode可以描述的多种节点类型,它们本质上都是VNode类的实例,只是在实例化的时候传入的属性参数不同而已。

3.3 VNode的作用

说了这么多,那么VNodeVue的整个虚拟DOM过程起了什么作用呢?

        其实VNode的作用是相当大的。我们在视图渲染之前,把写好的template模板先编译成VNode并缓存下来,等到数据发生变化页面需要重新渲染的时候,我们把数据发生变化后生成的VNode与前一次缓存下来的VNode进行对比,找出差异,然后有差异的VNode对应的真实DOM节点就是需要重新渲染的节点,最后根据有差异的VNode创建出真实的DOM节点再插入到视图中,最终完成一次视图更新。

4. 总结

        本章首先介绍了虚拟DOM的一些基本概念和为什么要有虚拟DOM,其实说白了就是以JS的计算性能来换取操作真实DOM所消耗的性能。接着从源码角度我们知道了在Vue中是通过VNode类来实例化出不同类型的虚拟DOM节点,并且学习了不同类型节点生成的属性的不同,所谓不同类型的节点其本质还是一样的,都是VNode类的实例,只是在实例化时传入的属性参数不同罢了。最后探究了VNode的作用,有了数据变化前后的VNode,我们才能进行后续的DOM-Diff找出差异,最终做到只更新有差异的视图,从而达到尽可能少的操作真实DOM的目的,以节省性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/253845.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用pygame生成红包封面

import pygame import sys# 初始化pygame pygame.init()# 设置红包封面尺寸 size width, height 640, 960 screen_color (255, 0, 0) # 红色背景# 创建窗口 screen pygame.display.set_mode(size) pygame.display.set_caption(红包封面)# 加载龙形图片 dragon_image pygam…

运维自动化bingo前端

项目目录结构介绍 项目创建完成之后&#xff0c;我们会看到bingo_web项目其实是一个文件夹&#xff0c;我们进入到文件夹内部就会发现一些目录和文件&#xff0c;我们简单回顾一下里面的部分核心目录与文件。 ├─node_modules/ # node的包目录&#xff0c;项目运行的依赖包…

【C++】模板入门教程

C模板是C编程中的重要利器&#xff0c;能够大大提高编码效率。函数模板和类模板是模板编程中的两个重要概念&#xff0c;它们允许我们编写独立于数据类型的通用代码。 本文将深入介绍函数模板和类模板的使用方法&#xff0c;以及它们的实现原理、匹配规则和注意事项&#xff0…

Python进阶--下载想要的格言(基于格言网的Python爬虫程序)

注&#xff1a;由于上篇帖子&#xff08;Python进阶--爬取下载人生格言(基于格言网的Python3爬虫)-CSDN博客&#xff09;篇幅长度的限制&#xff0c;此篇帖子对上篇做一个拓展延伸。 目录 一、爬取格言网中想要内容的url 1、找到想要的内容 2、抓包分析&#xff0c;找到想…

交友系统---让陌生人变成熟悉人的过程。APP小程序H5三端源码交付,支持二开。

随着社交网络的发展和普及&#xff0c;人们之间的社交模式正在发生着深刻的变革。传统的线下交友方式已经逐渐被线上交友取而代之。而同城交友正是这一趋势的产物&#xff0c;它利用移动互联网的便利性&#xff0c;将同城内的人们连接在一起&#xff0c;打破了时空的限制&#…

[计算机提升] 备份系统:系统映像

6.3 备份系统&#xff1a;系统映像 备份系统和还原系统是一套互补的操作。 操作系统的备份就是将操作系统当前的所有数据复制到硬盘的一个空闲区域&#xff0c;以防止系统崩溃或数据丢失。还原操作则是将先前备份的数据恢复到操作系统中&#xff0c;使系统回到之前的样子&…

Matplotlib绘制炫酷散点图:从二维到三维,再到散点图矩阵的完整指南与实战【第58篇—python:Matplotlib绘制炫酷散点图】

文章目录 Matplotlib绘制炫酷散点图&#xff1a;二维、三维和散点图矩阵的参数说明与实战引言二维散点图三维散点图散点图矩阵二维散点图进阶&#xff1a;辅助线、注释和子图三维散点图进阶&#xff1a;动画效果和交互性散点图矩阵进阶&#xff1a;调整样式和添加密度图总结与展…

【小白开服日记】幻兽帕鲁服务器如何搭建?

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现…

多线程基础

线程与进程 进程 进程是对运⾏时程序的封装&#xff0c;是系统进⾏资源调度和分配的基本单位&#xff0c;实现了操作系统的并发。程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至 CPU&#xff0c;数据加载至内存&…

Arthas使用教程—— 阿里开源线上监控诊断产品

文章目录 1 简介2背景3 图形界面工具 arthas 阿里开源3.1 &#xff1a;启动 arthas3.2 help :查看arthas所有命令3.3 查看 dashboard3.4 thread 列出当前进程所有线程占用CPU和内存情况3.5 jvm 查看该进程的各项参数 &#xff08;类比 jinfo&#xff09;3.6 通过 jad 来反编译 …

2-1 动手学深度学习v2-Softmax回归-笔记

回归 VS 分类 回归估计一个连续值分类预测一个离散类别 从回归到多类分类 回归 单连续数值输出输出的区间&#xff1a;自然区间 R \mathbb{R} R损失&#xff1a;跟真实值的区别 分类 通常多个输出&#xff08;这个输出的个数是等于类别的个数&#xff09;输出的第 i i i…

Flink Format系列(2)-CSV

Flink的csv格式支持读和写csv格式的数据&#xff0c;只需要指定 format csv&#xff0c;下面以kafka为例。 CREATE TABLE user_behavior (user_id BIGINT,item_id BIGINT,category_id BIGINT,behavior STRING,ts TIMESTAMP(3) ) WITH (connector kafka,topic user_behavior…

机器学习——有监督学习和无监督学习

有监督学习 简单来说&#xff0c;就是人教会计算机学会做一件事。 给算法一个数据集&#xff0c;其中数据集中包含了正确答案&#xff0c;根据这个数据集&#xff0c;可以对额外的数据希望得到一个正确判断&#xff08;详见下面的例子&#xff09; 回归问题 例如现在有一个…

17:定时器编程实战

1、实验目的 (1)使用定时器来完成LED闪烁 (2)原来实现闪烁时中间的延迟是用delay函数实现的&#xff0c;在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间&#xff08;譬如0.3s&#xff09;&#xff0c;在这个定时器定时时间内…

Visual Studio 2022中创建的C++项目无法使用万能头<bits/stdc++.h>解决方案

目录 发现问题 解决办法 第一步 第二步 第三步 第四步 最后一步 问题解决 发现问题 如果大家也遇到下面这种问题&#xff0c;可能是没有include文件夹中没有bits/stdc.h 解决办法 第一步 打开一个C项目&#xff0c;鼠标移动至头文件上右击&#xff0c;选择转到文档或…

嵌入式学习之Linux入门篇笔记——10,Linux连接档概念

配套视频学习链接&#xff1a;http://【【北京迅为】嵌入式学习之Linux入门篇】 https://www.bilibili.com/video/BV1M7411m7wT/?p4&share_sourcecopy_web&vd_sourcea0ef2c4953d33a9260910aaea45eaec8 目录 1.Linux 下的连接档种类 2.什么是 inode&#xff1f; 3.什…

【HarmonyOS应用开发】HTTP数据请求(十四)

文章末尾含相关内容源代码 一、概述 日常生活中我们使用应用程序看新闻、发送消息等&#xff0c;都需要连接到互联网&#xff0c;从服务端获取数据。例如&#xff0c;新闻应用可以从新闻服务器中获取最新的热点新闻&#xff0c;从而给用户打造更加丰富、更加实用的体验。 那么…

支持534种语言,开源大语言模型MaLA-500

无论是开源的LLaMA 2还是闭源的GPT系列模型&#xff0c;功能虽然很强大&#xff0c;但对语言的支持和扩展比较差&#xff0c;例如&#xff0c;二者都是以英语为主的大模型。 为了提升大模型语言的多元化&#xff0c;慕尼黑大学、赫尔辛基大学等研究人员联合开源了&#xff0c;…

Windows 安装 MySQL 最新最简教程

Windows 安装 MySQL 最新最简教程 官网地址 https://dev.mysql.com/downloads/mysql/下载 MySQL zip 文件 配置 MySQL1、解压文件 2、进入 bin 目录 搜索栏输入 cmd 回车进入命令行 C:\Users\zhong\Desktop\MySQL\mysql-8.3.0-winx64\mysql-8.3.0-winx64\bin 注意这里是你自己…