浏览器工作原理与实践--虚拟DOM:虚拟DOM和实际的DOM有何不同

虚拟DOM是最近非常火的技术,两大著名前端框架React和Vue都使用了虚拟DOM,所以我觉得非常有必要结合浏览器的工作机制对虚拟DOM进行一次分析。当然了,React和Vue框架本身所蕴含的知识点非常多,而且也不是我们专栏的重点,所以在这里我们还是把重心聚焦在虚拟DOM上。

在本文我们会先聊聊DOM的一些缺陷,然后在此基础上介绍虚拟DOM是如何解决这些缺陷的,最后再站在双缓存和MVC的视角来聊聊虚拟DOM。理解了这些会让你对目前的前端框架有一个更加底层的认识,这也有助于你更好地理解这些前端框架。

DOM的缺陷

通过前面一系列文章的学习,你对DOM的生成过程应该已经有了比较深刻的理解,并且也知道了通过JavaScript操纵DOM是会影响到整个渲染流水线的。另外,DOM还提供了一组JavaScript接口用来遍历或者修改节点,这套接口包含了getElementById、removeChild、appendChild等方法。

比如,我们可以调用document.body.appendChild(node)

虚拟DOM是最近非常火的技术,两大著名前端框架React和Vue都使用了虚拟DOM,所以我觉得非常有必要结合浏览器的工作机制对虚拟DOM进行一次分析。当然了,React和Vue框架本身所蕴含的知识点非常多,而且也不是我们专栏的重点,所以在这里我们还是把重心聚焦在虚拟DOM上。

在本文我们会先聊聊DOM的一些缺陷,然后在此基础上介绍虚拟DOM是如何解决这些缺陷的,最后再站在双缓存和MVC的视角来聊聊虚拟DOM。理解了这些会让你对目前的前端框架有一个更加底层的认识,这也有助于你更好地理解这些前端框架。

DOM的缺陷

通过前面一系列文章的学习,你对DOM的生成过程应该已经有了比较深刻的理解,并且也知道了通过JavaScript操纵DOM是会影响到整个渲染流水线的。另外,DOM还提供了一组JavaScript接口用来遍历或者修改节点,这套接口包含了getElementById、removeChild、appendChild等方法。

比如,我们可以调用document.body.appendChild(node)往body节点上添加一个元素,调用该API之后会引发一系列的连锁反应。首先渲染引擎会将node节点添加到body节点之上,然后触发样式计算、布局、绘制、栅格化、合成等任务,我们把这一过程称为重排。除了重排之外,还有可能引起重绘或者合成操作,形象地理解就是“牵一发而动全身”。另外,对于DOM的不当操作还有可能引发强制同步布局和布局抖动的问题,这些操作都会大大降低渲染效率。因此,对于DOM的操作我们时刻都需要非常小心谨慎。

当然,对于简单的页面来说,其DOM结构还是比较简单的,所以以上这些操作DOM的问题并不会对用户体验产生太多影响。但是对于一些复杂的页面或者目前使用非常多的单页应用来说,其DOM结构是非常复杂的,而且还需要不断地去修改DOM树,每次操作DOM渲染引擎都需要进行重排、重绘或者合成等操作,因为DOM结构复杂,所生成的页面结构也会很复杂,对于这些复杂的页面,执行一次重排或者重绘操作都是非常耗时的,这就给我们带来了真正的性能问题。

所以我们需要有一种方式来减少JavaScript对DOM的操作,这时候虚拟DOM就上场了。

什么是虚拟DOM

在谈论什么是虚拟DOM之前,我们先来看看虚拟DOM到底要解决哪些事情。

  • 将页面改变的内容应用到虚拟DOM上,而不是直接应用到DOM上。

  • 变化被应用到虚拟DOM上时,虚拟DOM并不急着去渲染页面,而仅仅是调整虚拟DOM的内部状态,这样操作虚拟DOM的代价就变得非常轻了。

  • 在虚拟DOM收集到足够的改变时,再把这些变化一次性应用到真实的DOM上。

基于以上三点,我们再来看看什么是虚拟DOM。为了直观理解,你可以参考下图:

虚拟DOM执行流程

该图是我结合React流程画的一张虚拟DOM执行流程图,下面我们就结合这张图来分析下虚拟DOM到底怎么运行的。

  • 创建阶段。首先依据JSX和基础数据创建出来虚拟DOM,它反映了真实的DOM树的结构。然后由虚拟DOM树创建出真实DOM树,真实的DOM树生成完后,再触发渲染流水线往屏幕输出页面。

  • 更新阶段。如果数据发生了改变,那么就需要根据新的数据创建一个新的虚拟DOM树;然后React比较两个树,找出变化的地方,并把变化的地方一次性更新到真实的DOM树上;最后渲染引擎更新渲染流水线,并生成新的页面。

既然聊到虚拟DOM的更新,那我们就不得不聊聊最新的React Fiber更新机制。通过上图我们知道,当有数据更新时,React会生成一个新的虚拟DOM,然后拿新的虚拟DOM和之前的虚拟DOM进行比较,这个过程会找出变化的节点,然后再将变化的节点应用到DOM上。

这里我们重点关注下比较过程,最开始的时候,比较两个虚拟DOM的过程是在一个递归函数里执行的,其核心算法是reconciliation。通常情况下,这个比较过程执行得很快,不过当虚拟DOM比较复杂的时候,执行比较函数就有可能占据主线程比较久的时间,这样就会导致其他任务的等待,造成页面卡顿。为了解决这个问题,React团队重写了reconciliation算法,新的算法称为Fiber reconciler,之前老的算法称为Stack reconciler。

在前面《async/await:使用同步的方式去写异步代码》那篇文章中我们介绍了协程,其实协程的另外一个称呼就是Fiber,所以在这里我们可以把Fiber和协程关联起来,那么所谓的Fiber reconciler相信你也很清楚了,就是在执行算法的过程中出让主线程,这样就解决了Stack reconciler函数占用时间过久的问题。至于具体的实现过程在这里我就不详细分析了,如果感兴趣的话,你可以自行查阅相关资料进行学习。

了解完虚拟DOM的大致执行流程,你应该也就知道为何需要虚拟DOM了。不过以上都从单纯的技术视角来分析虚拟DOM的,那接下来我们再从双缓存和MVC模型这两个视角来聊聊虚拟DOM。

1. 双缓存

在开发游戏或者处理其他图像的过程中,屏幕从前缓冲区读取数据然后显示。但是很多图形操作都很复杂且需要大量的运算,比如一幅完整的画面,可能需要计算多次才能完成,如果每次计算完一部分图像,就将其写入缓冲区,那么就会造成一个后果,那就是在显示一个稍微复杂点的图像的过程中,你看到的页面效果可能是一部分一部分地显示出来,因此在刷新页面的过程中,会让用户感受到界面的闪烁。

而使用双缓存,可以让你先将计算的中间结果存放在另一个缓冲区中,等全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区,这样就使得整个图像的输出非常稳定。

在这里,你可以把虚拟DOM看成是DOM的一个buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到DOM上,这样就能减少一些不必要的更新,同时还能保证DOM的稳定输出。

2. MVC模式

到这里我们了解了虚拟DOM是一种类似双缓存的实现。不过如果站在技术角度来理解虚拟缓存,依然不能全面理解其含义。那么接下来我们再来看看虚拟DOM在MVC模式中所扮演的角色。

在各大设计模式当中,MVC是一个非常重要且应用广泛的模式,因为它能将数据和视图进行分离,在涉及到一些复杂的项目时,能够大大减轻项目的耦合度,使得程序易于维护。

关于MVC的基础结构,你可以先参考下图:

MVC基础结构

通过上图你可以发现,MVC的整体结构比较简单,由模型、视图和控制器组成,其核心思想就是将数据和视图分离,也就是说视图和模型之间是不允许直接通信的,它们之间的通信都是通过控制器来完成的。通常情况下的通信路径是视图发生了改变,然后通知控制器,控制器再根据情况判断是否需要更新模型数据。当然还可以根据不同的通信路径和控制器不同的实现方式,基于MVC又能衍生出很多其他的模式,如MVP、MVVM等,不过万变不离其宗,它们的基础骨架都是基于MVC而来。

所以在分析基于React或者Vue这些前端框架时,我们需要先重点把握大的MVC骨架结构,然后再重点查看通信方式和控制器的具体实现方式,这样我们就能从架构的视角来理解这些前端框架了。比如在分析React项目时,我们可以把React的部分看成是一个MVC中的视图,在项目中结合Redux就可以构建一个MVC的模型结构,如下图所示:

基于React和Redux构建MVC模型

在该图中,我们可以把虚拟DOM看成是MVC的视图部分,其控制器和模型都是由Redux提供的。其具体实现过程如下:

  • 图中的控制器是用来监控DOM的变化,一旦DOM发生变化,控制器便会通知模型,让其更新数据;

  • 模型数据更新好之后,控制器会通知视图,告诉它模型的数据发生了变化;

  • 视图接收到更新消息之后,会根据模型所提供的数据来生成新的虚拟DOM;

  • 新的虚拟DOM生成好之后,就需要与之前的虚拟DOM进行比较,找出变化的节点;

  • 比较出变化的节点之后,React将变化的虚拟节点应用到DOM上,这样就会触发DOM节点的更新;

  • DOM节点的变化又会触发后续一系列渲染流水线的变化,从而实现页面的更新。

在实际工程项目中,你需要学会分析出这各个模块,并梳理出它们之间的通信关系,这样对于任何框架你都能轻松上手了。

总结

好了,今天就介绍到这里,下面我来总结下本文的主要内容。

首先我们分析了直接操作DOM会触发渲染流水线的一系列反应,如果对DOM操作不当的话甚至还会触发强制同步布局和布局抖动的问题,这也是我们在操作DOM时需要非常小心谨慎的原因。

在此分析的基础上,我们介绍了虚拟DOM是怎么解决直接操作DOM所带来的问题以及React Fiber更新机制。

要聊前端框架,就绕不开设计模式,所以接下来我们又从双缓存和MVC角度分析了虚拟DOM。双缓存是一种经典的思路,应用在很多场合,能解决页面无效刷新和闪屏的问题,虚拟DOM就是双缓存思想的一种体现。而基于MVC的设计思想也广泛地渗透到各种场合,并且基于MVC又衍生出了很多其他模式(如MVP、MVVM等),不过万变不离其宗,它们的基础骨架都是基于MVC而来。站在MVC视角来理解虚拟DOM能让你看到更为“广阔的世界”。

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

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

相关文章

JavaWeb前端基础(HTML CSS JavaScript)

本文用于检验学习效果&#xff0c;忘记知识就去文末的链接复习 1. HTML 1.1 HTML基础 结构 头<head>身体<body> 内容 图片<img>段落<p>图标<link> 标签 单标签双标签 常用标签 div&#xff1a;分割块span&#xff1a;只占需要的大小p&…

sqlserver问题记录

今天在利用sql查询数据时出现如下错误 在执行批处理时出现错误。错误消息为: 引发类型为“System.OutOfMemoryException”的异常。 症状 使用 SSMS 运行返回大量数据的 SQL 查询时&#xff0c;会收到类似于以下内容的错误消息&#xff1a; 执行批处理时出错。 错误消息为&…

nginx工作原理解析

目录 1、master-workers 的工作机制介绍 2、master-workers 的机制的好处 3、设置多少个 worker 4、最大连接数和支持的最大并发数的计算 1、master-workers 的工作机制介绍 nginx在启动后&#xff0c;会有一个master进程和一个或者多个相互独立的worker进程 过来的请求由…

C++模仿qq界面

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口的大小this->resize(645,497);//设置窗口名字this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(QIcon("C:\\zhouzhouMyfile\\qt_proj…

Linux网络编程: TCP协议之SACK与D-SACK详解

一、参考RFC https://www.ietf.org/rfc/rfc2018 https://www.ietf.org/rfc/rfc2883.txt 二、SACK选项&#xff08;RFC2018&#xff09; SACK实现的需要发送方和接收方协作。为此&#xff0c;TCP首部实际上定义了两种选项&#xff1a;SACK允许选项、SACK选项。 SACK允许选项…

突破校园网限速:使用 iKuai 多拨分流负载均衡 + Clash 代理(内网带宽限制通用)

文章目录 1. 简介2. iKuai 部署2.1 安装 VMware2.2 安装 iKuai(1) 下载固件(2) 安装 iKuai 虚拟机(3) 配置 iKuai 虚拟机(4) 配置 iKuai(5) 配置多拨分流 2.3 测试速度 3. Clash 部署(1) 配置磁盘分区(2) 安装 Docker(3) 安装 Clash(4) 设置代理 4. 热点&#xff1a;一起瓜分互…

Redis基本概念

什么是Redis Redis&#xff08;Remote Dictionary Server &#xff09;&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 Redis的用处 缓存 缓存现在几…

shell脚本2

变量 变量是在程序中保存用户数据的一段内存存储空间&#xff0c;变量名是内存空间的首地址 字母、数字、下划线组成&#xff0c;不能以数字开头 原则&#xff1a;直接使用&#xff0c;不需要变量声明 格式&#xff1a;变量名 变量的值 环境变量 关闭窗口即会失效 若要永久生…

数据结构—红黑树

红黑树介绍 红黑树&#xff08;Red Black Tree&#xff09;是一种自平衡二叉查找树。由于其自平衡的特性&#xff0c;保证了最坏情形下在 O(logn) 时间复杂度内完成查找、增加、删除等操作&#xff0c;性能表现稳定。 在 JDK 中&#xff0c;TreeMap、TreeSet 以及 JDK1.8 的 …

数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成

数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成 目录 数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成生成效果基本描述模型描述程序设计参考资料 生成效果 基本描述 1.Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成&#xf…

深度学习实践(一)基于Transformer英译汉模型

本文目录 前述一、环境依赖二、数据准备1. 数据加载程序解析word_tokenize()将字符串分割为一个个的单词&#xff0c;并由列表保存。 2. 构建单词表程序解析&#xff08;1&#xff09;将列表里每个子列表的所有单词合并到一个新列表&#xff08;没有子列表&#xff09;中。&…

RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表

RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表 文章目录 RabbitMQ3.13.0起支持MQTT5.0协议及MQTT5.0特性功能列表1. MQTT概览2. MQTT 5.0 特性1. 特性概要2. Docker中安装RabbitMQ及启用MQTT5.0协议 3. MQTT 5.0 功能列表1. 消息过期1. 描述2. 举例3. 实现 2. 订阅标识…

浅聊java集合框架中的java.util.LinkedList

java集合框架总览 Java集合框架是一个用来代表和操纵集合的统一架构&#xff0c;它为管理和组织对象的集合提供了一组类和接口。这个框架包含三个主要部分&#xff1a;接口、实现和算法。 接口&#xff1a; Collection&#xff1a;这是集合框架的根接口&#xff0c;定义了集…

亚马逊运营必看!如何运用自养号测评获得买家评论转销量?

作为亚马逊卖家&#xff0c;相信大家对亚马逊的产品星级评分 (Rating) 都不陌生&#xff0c;这几颗亮眼的星星&#xff0c;不仅可以让你的Listing脱颖而出&#xff0c;获得足够多、足够高的产品评分&#xff0c;也是促使消费者下单的重要因素之一。 那么&#xff0c;亚马逊运营…

3D可视化技术亮相高铁站,引领智慧出行新潮流

在科技飞速发展的今天&#xff0c;我们的生活正经历着前所未有的变革。高铁站作为现代交通的重要枢纽&#xff0c;也在不断地创新和进步。 3D可视化技术通过三维立体的方式&#xff0c;将高铁站内部和外部的结构、设施、流线等以更加直观、生动的形式呈现出来。乘客们只需通过手…

全国高等学校sql

教育部颁发的最新高等学校名单&#xff0c;sql已整理好(按照省份树形结构)&#xff0c;是mysql8版本的 全国高等学校:预览地址&#xff1a;https://kdocs.cn/l/ckaFzCWMV1jn sql下载地址&#xff1a; https://pan.imgbed.link/file/22581

mac/win使用pyinstaller打包app/exe文件,活着执行脚本,双击运行

&#x1f338; 踩坑记录 python环境最好使用虚拟环境&#xff0c;推荐使用conda管理&#xff0c;并且若本地有python环境&#xff0c;不要使用和 本地环境版本 相同的虚拟环境 这里踩坑较多&#xff0c;已经记不清楚注意点 虚拟环境python版本不要和本地环境一样 mac/win只能…

匿名信一封来信一封云来信表白祝福道歉短信H5公众号,小程序系统搭建(搭建赠送人工传话系统+主机管理面板)

“一封来信”是最近某音上爆火的一个活动话题&#xff0c;可以通过H5网站&#xff0c;编辑自己想要对某人说的话或者祝福&#xff0c;网站会把您想说的发给您预留的号码&#xff0c;可以特定时间&#xff0c;特定话题。 最近的兴起是给朋友或暗恋的人发送新年祝福&#xff0c;…

Leetcode算法训练日记 | day21

一、二叉搜索树的最小绝对差 1.题目 Leetcode&#xff1a;第 530 题 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,…

『大模型笔记』LLMs入门:从头理解与编码LLM的自注意力机制

LLMs入门&#xff1a;从头理解与编码LLM的自注意力机制 这里直接引用我语雀上的的文章&#xff1a;《从头理解与编码LLM的自注意力机制》