浏览器工作原理与实践--虚拟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.mzph.cn/news/804864.shtml

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

相关文章

二手车商的套路

https://www.dongchedi.com/article/7126394624675578405 https://www.dongchedi.com/article/7126394624675578405 现在,有越来越多的人去了解二手车,二手车相对于新车来说,更加的亲民划算。很多新车需要四五十万,而二手车有可…

Qt Creator 新建项目

🐌博主主页:🐌​倔强的大蜗牛🐌​ 📚专栏分类:QT❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、使用 Qt Creator 新建项目 1、新建项目 2、选择项目模板 3、选择项目路径 4、选择构建系统 5…

你不可不知的5款小众神器

Hey小伙伴们👋,是不是还在为工作中的效率不高而烦恼呢?别急,今天我就给大家安利5款超实用的国产小众工作效软件,让你的工作台效率翻倍,轻松应对各种工作挑战!🌟 1️⃣ 亿可达 ycoda…

视频图像的两种表示方式YUV与RGB(4)

本篇主要讲YUV与RGB之间的转换,包括YUV444 颜色编码格式 转为 RGB 格式 ,RGB颜色编码格式转为 YUV444 格式。 一、 YUV与RGB之间的转换 YUV与RGB颜色格式之间进行转换时 , 涉及一系列的数学运算 ; YUV 颜色编码格式转为RGB格式的转换公式 取决于 于 YUV …

蓝桥杯每日一题:矩形牛棚(单调栈)

作为一个资本家,农夫约翰希望通过购买更多的奶牛来扩大他的牛奶业务。 因此,他需要找地方建立一个新的牛棚。 约翰购买了一大块土地,这个土地可以看作是一个 R行(编号 1∼R1)C 列(编号 1∼C1)…

Python requests 模块

爬虫、网络编程、接口......对于Python工程师来讲都绕不过一个强大的模块---requests,本篇文章就深入详细讲一讲requests模块。同时也先也分享一下开源API网站:Gitee-API文档、JSONPlaceholder API文档、和风天气API文档、Postman Echo API网站&#xff…

C语言_文件操作

文件基础 什么是文件 文件是在计算机中以实现某种功能、或某个软件的部分功能为目的而定义的一个单位。磁盘上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分的)。 程序文件 …

App加固:不同类型和费用对比

文章目录 [TOC]引言应用程序加固是什么不同类型[App加固](https://www.ipaguard.com/)的费用对比基础加固高级加固云加固 白嫖的混淆加密工具](https://www.ipaguard.com/)-[ipaguard总结参考资料 引言 在当前移动应用市场中,安全性已经成为一个非常重要的话题。为…

tdesign坑之EnhancedTable树形结构默认展开所有行

⚠️在官方实例中,树形结构的表格提供了2种方法控制展开全部节点: 一是通过配置属性tree.defaultExpandAll为true代表默认展开全部节点(仅默认情况有效); 二是使用组件实例方法expandAll()可以自由控制树形结构的展开…

Bert基础(十二)--Bert变体之知识蒸馏原理解读

B站视频:https://www.bilibili.com/video/BV1nx4y1v7F5/ 白话知识蒸馏 在前面,我们了解了BERT的工作原理,并探讨了BERT的不同变体。我们学习了如何针对下游任务微调预训练的BERT模型,从而省去从头开始训练BERT的时间。但是&#…

311_C++_QT表格的单个item的撤销、恢复操作,代码量很小,轻量化

:初始截图 :修改截图 :撤销截图 connect(m_customTableWidget, &QTableWidget::itemChanged, this, &FileDoc::itemChanged);

离心机租赁,冷冻离心机租借,实验室离心机租售,大容量离心机以租代买

离心机租赁,冷冻离心机租借,实验室离心机租售,大容量离心机以租代买,满足广大客户对离心的租赁与购买需求! 服务优势 1、以租代买:租期达到一定期限,租金抵扣货款; 2、快速上门:2分钟响应,同城12小时内到…

短剧在线搜索PHP网站源码

源码简介 短剧在线搜索PHP网站源码,自带本地数据库500数据,共有6000短剧视频,与短剧猫一样。 搭建环境 PHP 7.3 Mysql 5.6 安装教程 1.上传源码到网站目录中 2.修改【admin.php】中, $username ‘后台登录账号’; $passwor…

【C++】lambda表达式

目录 一、lambda表达式1.1 C98中的例子1.2 lambda表达式语法1.3 函数对象与lambda表达式 一、lambda表达式 1.1 C98中的例子 如果要对一个数据集合进行排序,可以使用sort函数: int main() {int array[] { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较…

Vue-Router入门

现在的前后端分离项目,后端只管数据传递,视图跳转的活交由前端来干了,vue-router就是专门来干这个活的,它可以让页面跳转到指定组件 组件是可复用的 Vue 实例, 把一些公共的模块抽取出来,然后写成单独的的工具组件或者…

面对DDOS攻击,有哪些解决办法

随着互联网带宽的持续增长以及DDOS黑客技术的发展,DDOS拒绝服务攻击的实施变得愈发容易。商业竞争、打击报复、网络敲诈等多种因素,各行各业的用户都曾受到DDOS攻击的威胁。 一旦遭受到DDOS攻击,随之而来的就是业务宕机,用户无法…

44.网络游戏逆向分析与漏洞攻防-角色管理功能通信分析-角色创建服务器反馈数据包分析

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 如果看不懂、不知道现在做的什么,那就跟着做完看效果 现在的代码都是依据数据包来写的,如果看不懂代码,就说明没看懂数据包…

转让北京装饰装修二级和建筑幕墙施工二级流程和条件

我公司可以帮您快速办理北京市各类建筑姿质申请,也有现成的姿质转让,新申请建筑姿质要求比较高,但是对于企业来说,承接模板脚手架工程也是需要具备姿质的,该姿质也就是模板脚手架姿质,那么对于企业想要申请…

基于Java+SpringBoot+Vue幼儿园管理系统(源码+文档+部署+讲解)

一.系统概述 随着信息时代的来临,过去的传统管理方式缺点逐渐暴露,对过去的传统管理方式的缺点进行分析,采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术,提出了一种教 学生信息、学生考勤、健康记录…

Fecify 商品标签功能

关于商品标签 商品标签是指商家可以在展示商品时,自己创建一个自定义标签,可自定义某个关键词或短语。这样顾客在浏览商城时,只需要通过标签就能看到更直观的展示信息。 商品标签可以按照用户的属性、行为、偏好等进行分类,标签要…