React 第三十二章 虚拟DOM

面试题:什么是虚拟DOM?其优点有哪些?
标准且浅显的答案
虚拟dom本质上就是一个普通的 JS 对象,用于描述视图的界面结构

虚拟 DOM 最早是由 React 团队提出来的,因此 React 团队在对虚拟 DOM 的定义上面有绝对的话语权。可以参考https://legacy.reactjs.org/docs/faq-internals.html

Virtual DOM 是一种编程概念 。在这个概念里, UI 以一种理想化的,或者说“虚拟的”表现形式被保存于内存中。

也就是说,只要我们有一种方式,能够将真实 DOM 的层次结构描述出来,那么这就是一个虚拟 DOM。

image-20230223114000816

在 React 中,React 团队使用的是 JS 对象来对 DOM 结构进行一个描述。但是很多人会直接把 JS 对象和虚拟 DOM 划等号,这种理解是不太准确的,比较片面的。

虚拟 DOM 和 JS 对象之间的关系:前者是一种思想,后者是一种思想的具体实现。

为什么需要虚拟 DOM

使用虚拟 DOM 主要有两个方面的优势:

  • 相较于 DOM 的体积优势和速度优势
  • 多平台的渲染抽象能力

相较于 DOM 的体积优势和速度优势

首先我们需要明确一个点,JS 层面的计算速度要比 DOM 层面的计算要快:

  • DOM 对象最终要被浏览器渲染出来之前,浏览器会有很多工作要做(浏览器的渲染原理)
  • DOM 对象上面的属性也非常非常多
const div = document.createElement("div");
for(let i in div){console.log(i + " ")}

操作 JS 对象的时间和操作 DOM 对象的时间是完全不一样的。

JS 层面的计算速度要高于 DOM 层面的计算速度。

此时有一个问题:虽然使用了 JS 对象来描述 UI,但是最终不还是要用原生 DOM API 去操作 DOM 么?

虚拟 DOM 在第一次渲染页面的时候,并没有什么优势,速度肯定比直接操作原生 DOM API 要慢一些,虚拟 DOM 真正体现优势是在更新阶段。

根据 React 团队的研究,在渲染页面时,相比使用原生 DOM API,开发人员更加倾向于使用 innerHTML

let newP = document.createElement("p");
let newContent = document.createTextNode("this is a test");
newP.appendChild(newContent);
document.body.appendChild(newP);
document.body.innerHTML = `<p>this is a test</p>
`;

因此在使用 innerHTML 的时候,就涉及到了两个层面的计算:

  • JS 层面:解析字符串
  • DOM 层面:创建对应的 DOM 节点

接下来我们加入虚拟 DOM 来进行对比:

innerHTML虚拟 DOM
JS 层面计算解析字符串创建 JS 对象
DOM 层面计算创建对应的 DOM 节点创建对应的 DOM 节点

虚拟 DOM 真正发挥威力的时候,是在更新阶段

innerHTML 进行更新的时候,要全部重新赋值,这意味着之前创建的 DOM 节点需要全部销毁掉,然后重新进行创建

但是虚拟 DOM 只需要更新必要的 DOM 节点即可

innerHTML虚拟 DOM
JS 层面计算解析字符串创建 JS 对象
DOM 层面计算销毁原来所有的 DOM 节点修改必要的 DOM 节点
DOM 层面计算创建对应的 DOM 节点

多平台的渲染抽象能力

UI = f(state)这个公式进一步进行拆分可以拆分成两步:

  • 根据自变量的变化计算出 UI
  • 根据 UI 变化执行具体的宿主环境的 API

虚拟 DOM 只是多真实 UI 的一个描述,回头根据不同的宿主环境,可以执行不同的渲染代码:

  • 浏览器、Node.js 宿主环境使用 ReactDOM 包
  • Native 宿主环境使用 ReactNative 包
  • Canvas、SVG 或者 VML(IE8)宿主环境使用 ReactArt 包
  • ReactTest 包用于渲染出 JS 对象,可以很方便地测试“不隶属于任何宿主环境的通用功能”

React 中的虚拟DOM

在 React 中通过 JSX 来描述 UI,JSX 最终会被转为一个叫做 createElement 方法的调用,调用该方法后就会得到虚拟 DOM 对象。

经过 Babel 编译后结果如下:

image-20221226155735808

在源码中 createElement 方法如下:

/**** @param {*} type 元素类型 h1* @param {*} config 属性对象 {id : "aa"}* @param {*} children 子元素 hello* @returns* <h1 id="aa">hello</h1>*/
export function createElement(type, config, children) {let propName;const props = {};let key = null;let ref = null;let self = null;let source = null;// 说明有属性if (config != null) {// ...for (propName in config) {if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}// 经历了上面的 if 之后,所有的属性都放到了 props 对象上面// props ==> {id : "aa"}// children 可以有多个参数,这些参数被转移到新分配的 props 对象上// 如果是多个子元素,对应的是一个数组const childrenLength = arguments.length - 2;if (childrenLength === 1) {props.children = children;} else if (childrenLength > 1) {const childArray = Array(childrenLength);for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}// ...props.children = childArray;}// 添加默认的 propsif (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}// ...return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props);
}const ReactElement = function (type, key, ref, self, source, owner, props) {// 该对象就是最终向外部返回的 vdom(也就是用来描述 DOM 层次结构的 JS 对象)const element = {// 让我们能够唯一地将其标识为 React 元素$$typeof: REACT_ELEMENT_TYPE,// 元素的内置属性type: type,key: key,ref: ref,props: props,// 记录负责创建此元素的组件。_owner: owner,};// ...return element;
};

在上面的代码中,最终返回的 element 对象就是我们所说的虚拟 DOM 对象。在官方文档中,官方更倾向于将这个对象称之为 React 元素。

真题解答

题目:什么是虚拟DOM?其优点有哪些?

参考答案:

虚拟 DOM 最初是由 React 团队所提出的概念,这是一种编程的思想,指的是针对真实 UI DOM 的一种描述能力。

在 React 中,使用了 JS 对象来描述真实的 DOM 结构。虚拟DOM和 JS 对象之间的关系:前者是一种思想,后者是这种思想的具体实现。

使用虚拟 DOM 有如下的优点:

  • 相较于 DOM 的体积和速度优势
  • 多平台渲染的抽象能力

相较于 DOM 的体积和速度优势

  • JS 层面的计算的速度,要比 DOM 层面的计算快得多
    • DOM 对象最终要被浏览器显示出来之前,浏览器会有很多工作要做(浏览器渲染原理)
    • DOM 上面的属性也是非常多的
  • 虚拟 DOM 发挥优势的时机主要体现在更新的时候,相比较 innerHTML 要将已有的 DOM 节点全部销毁,虚拟 DOM 能够做到针对 DOM 节点做最小程度的修改

多平台渲染的抽象能力

  • 浏览器、Node.js 宿主环境使用 ReactDOM 包
  • Native 宿主环境使用 ReactNative 包
  • Canvas、SVG 或者 VML(IE8)宿主环境使用 ReactArt 包
  • ReactTest 包用于渲染出 JS 对象,可以很方便地测试“不隶属于任何宿主环境的通用功能”

在 React 中,通过 JSX 来描述 UI,JSX 仅仅是一个语法糖,会被 Babel 编译为 createElement 方法的调用。该方法调用之后会返回一个 JS 对象,该对象就是虚拟 DOM 对象,官方更倾向于称之为一个 React 元素。

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

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

相关文章

若依-生成主子表

1. sql语句建表导入到数据库中&#xff1a; -- ---------------------------- -- Table structure for t_ques————主表 -- ----------------------------CREATE TABLE ques (ques_id INT NOT NULL AUTO_INCREMENT COMMENT Id,name VARCHAR(255) NOT NULL COMMENT 测评名称…

未授权访问:Rsync 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、利用rsync下载任意文件 5、利用rsync反弹shell 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c…

ApiHug - 闭门造车, 出门合辙

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Nex…

RocketMQ:新增consumer消费组group从最新消息开始消费skip last offset message

场景 想创建一个新的consumer去消费一个已经再使用的topic时&#xff0c;默认情况下会从topic中的第一条消息开始消费&#xff0c;大多数情况是需要从最新的消息开始。然后再使用CONSUME_FROM_LAST_OFFSET设置时并不会对新的consumer生效&#xff0c;它只是在停用consumer重新启…

Vue学习之:在 vue2 中引入 pdf.js 并配置使其能工作

安装 不同版本的 pdfjs 在 node_modules 中的目录不太一样&#xff0c;如果你想让他正常运行就按照我下面的来确保你的 nvm 版本是 18.17 如果不是的话&#xff0c;建议你配置跟我调成一样的&#xff0c;否则很容易出问题 nvm install 18.17.0 nvm use 18.17.0安装 pdfjs&…

代码随想录算法训练营day25 | 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

669. 修剪二叉搜索树 看题解做出来的 class Solution:def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:if not root:return Noneif root.val < low:return self.trimBST(root.right, low, high)if root.val > high:return…

【栈的应用】中缀和后缀表达式 {中缀转后缀表达式;中缀表达式求值;后缀表达式求值}

中缀转后缀表达式 在中缀变后缀时&#xff0c;操作数的顺序不会发生变化&#xff0c;只有运算符的顺序可能发生变化。同时又没有括号。所以在转换的过程中&#xff0c;只要碰到操作数&#xff0c;可以直接输出&#xff0c;而遇到运算符和括号进行相应的处理即可。 转换原则如…

SD8063音频功率放大器5V音频功率放大器低功耗

SD8063是一个双桥接音频功率放大器&#xff0c;当 连接到一个5V的pp时&#xff0c;将ver2.2W toa4ΩL oad(No电话)or2.5瓦至a3欧洛a(第2项) THDN含量小于an1.0%此外&#xff0c;耳机输入引 脚允许放大器在单端模式下工作时&#xff0c;驾驶立 体声耳机。 Boomer音频功率放大器的…

Java静态方法和实例方法有何不同?

在Java中&#xff0c;方法可以分为静态方法和实例方法。这两种方法的区别在于它们的调用方式、访问权限以及应用场景。理解这两者的区别对于掌握Java编程非常重要。下面我们将详细剖析这两者的区别&#xff0c;并通过代码示例加以说明。 1. 调用方式 静态方法的调用 静态方法…

代码随想录算法训练营day26 | 77. 组合

理论 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯法并不是什么高效的算法。因为回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&#xff0c;如果想让回溯法高效一些&#xff0c;可以加一些剪枝的操作&#xff0c;但也改…

MySQL单表查询案例演示

目录 一、创建数据库lianxi 二、选择数据库为lianxi 三、新建一个数据表grade&#xff0c;在grade表中插入数据 四、开始进行查询操作&#xff08;验证表中数据&#xff09; 1、查询1945班的成绩信息 2、查询1945班&#xff0c;语文成绩大于60小于90的成绩信息 3、查询学…

优雅谈论大模型8:神经网络与矩阵

向量与矩阵 上个章节的神经网络是为了解Transformer或者Mamba做好铺垫&#xff0c;在和后辈交流过程中发现有个障碍&#xff0c;那就是向量和矩阵。其实向量和矩阵的表达方式不是所有人都很习惯。在继续下面的章节之前小编认为有必要将向量、矩阵和神经网络做下补充解释。 向…

18.双线性插值缩放算法的matlab与FPGA实现

一篇文章为你讲透双线性插值 简介 1.什么是插值 图片放大是图像处理中的一个特别基础的操作。几乎在每一个图片相关的项目中&#xff0c;从传统图像处理到i深度学习&#xff0c;都有应用。   简单来说&#xff0c;插值指利用已知的点来“猜”未知的点&#xff0c;图像领域插…

RocketMQ 面试题(三)

1. RocketMQ如何处理大量积压的消息&#xff1f; RocketMQ在处理大量积压的消息时&#xff0c;会采取一系列策略来确保消息能够被及时消费并减少积压的情况。以下是一些处理大量积压消息的关键措施&#xff1a; 增加消费者实例或消费者组&#xff1a;通过增加消费者实例或消费…

华为OD机试【分奖金】(java)(100分)

1、题目描述 公司老板做了一笔大生意&#xff0c;想要给每位员工分配一些奖金&#xff0c;想通过游戏的方式来决定每个人分多少钱。按照员工的工号顺序&#xff0c;每个人随机抽取一个数字。按照工号的顺序往后排列&#xff0c;遇到第一个数字比自己数字大的&#xff0c;那么&…

C++ STL概念之 仿函数(函数对象)/ 空间配置器 / 适配器 / 理解STL

仿函数&#xff08;函数对象&#xff09; 什么是仿函数 仿函数&#xff0c;或称为函数对象&#xff0c;在C中是通过重载operator()的类实例&#xff0c;使得类的实例能够像函数一样被调用。 可调用对象 函数指针&#xff08;Function Pointers&#xff09;: 这是指向函数的指…

【class9】人工智能初步(处理单张图片)

Class9的任务&#xff1a;处理单张图像 为了更高效地学习&#xff0c;我们将“处理单张图像”拆分成以下几步完成&#xff1a; 1. 读取图像文件 2. 调用通用物体识别 3. 提取图像分类信息 4. 对应分类文件夹还未创建时&#xff0c;创建文件夹 5. 移动图像到对应文件夹 0.获取…

Ubuntu 安装 eclipse 的详细过程及工程创建和编译配置

目录 一、安装环境二、下载依赖 java jdk三、下载 eclipse四、安装4.1 java 环境4.2 eclipse 安装4.3 打开 eclipse 五、配置 eclipse5.1 新建 C 工程5.2 工具链 配置5.3 头文件路径5.4 链接库5.5 编译 一、安装环境 Ubuntu 版本&#xff1a;22.04.3 位数&#xff1a;64-bit 二…

记录一次 vue2 前端项目整合过程

整合成功效果图 具体说明&#xff1a; 项目A是现在的vue2前端项目&#xff0c;项目B是一个开源的工作流前端&#xff0c;项目后端代码已经整合了&#xff0c;就不多提了。这里主要记录下前端整合的过程和思路。 1、开源工作流里面的功能&#xff0c;拷贝到自己对应的vue2项目里…

PHP常量[特性,定义,输出,内置常量]

常量是一个在程序执行期间不能被修改的值。 特性 名称&#xff1a;常量的名称遵循大小写敏感的规则&#xff0c;但它们都推荐使用大写字母和下划线来增强可读性。值&#xff1a;常量的值可以是数字、字符串或表达式的结果&#xff0c;但一旦定义&#xff0c;就不能被改变。作…