(虚拟DOM)前端八股文修炼Day10

news2024/11/25 20:20:37

在这里插入图片描述

一 虚拟 DOM 是什么

虚拟 DOM (Virtual DOM) 本质上是真实 DOM 的一个轻量级的 JavaScript 表示形式。它是一个在内存中的抽象,用于描述真实 DOM 的结构和内容。虚拟 DOM 提供了一种机制,允许开发者通过操作 JavaScript 对象来间接更新页面,而不是直接操作更加繁重和耗费性能的真实 DOM 元素。

虚拟 DOM 的核心组成:

  • 节点对象: 虚拟 DOM 树中的每个节点对应于真实 DOM 树中的一个节点。这些节点对象可能代表 HTML 元素、文本内容或者是组件。

  • 属性和子节点: 虚拟 DOM 节点存储了对应真实 DOM 节点的属性(如 id、class、style 等)和子节点信息,以确保可以准确地映射到真实的 DOM 结构。

虚拟 DOM 的工作方式:

  1. 初始化渲染: 应用首次加载时,根据应用的初始状态,构建一棵完整的虚拟 DOM 树。

  2. 状态更新: 当应用状态发生变化时(例如,用户输入或从后端获取数据),框架会创建一个新的虚拟 DOM 树,这个树反映了最新的应用状态。

  3. 差异比较(Diffing): 框架比较新旧虚拟 DOM 树之间的差异。这个过程被称为 Diffing,它识别出实际需要在真实 DOM 中更新的部分。

  4. 批量更新(Patching): 根据差异比较的结果,框架计算出最高效的方式来更新真实 DOM。这一步骤确保只对真实 DOM 中实际改变的部分进行操作,从而最大限度减少性能消耗。

虚拟 DOM 的优点:

  • 性能提升: 通过减少直接操作真实 DOM 的次数,虚拟 DOM 机制显著提高了网页的响应速度和性能。

  • 跨平台: 虚拟 DOM 不依赖于浏览器 API,使其容易被移植到其他平台,如 React Native 使用虚拟 DOM 来构建原生应用。

  • 易于理解和维护的 UI 逻辑: 开发者可以专注于数据的状态和应用逻辑,而不必担心具体的 DOM 操作细节。这使得代码更加易于理解和维护。

虚拟 DOM 是现代前端框架中用于提高应用性能和开发效率的关键技术之一。

二 React diff 算法的原理是什么

React 的 Diff 算法是 React 虚拟 DOM 技术中的核心机制,它用于高效地对比前后两次虚拟 DOM 树的差异,并将这些差异应用到真实的 DOM 上,以实现快速的 UI 更新。React 的 Diff 算法基于两个主要假设:

  1. 两个不同类型的元素会产生不同的树。 当根节点类型不同时,React 会拆卸原有的树并建立新树。例如,一个<div>元素变成了<span>,或者一个组件变成了另一个不同的组件,React 将会销毁旧的树并完整地构建新树。

  2. 通过开发者指定的 key 属性,可以在不同的渲染中保持元素的稳定性。 在处理动态子元素集合时,开发者应该给每个列表项分配一个唯一的 key 属性,这样 React 可以重新使用和重新排序现有的子元素,而不是重新创建它们。

React Diff 算法的工作原理细分为三个层次:

1. 树的层级对比:
  • React 首先对比两棵树的根节点。
  • 如果根节点的类型不同,React 会销毁旧树,并从头开始构建新树。
  • 如果根节点的类型相同,React 会保留根节点,并递归地对比并更新其子节点。
2. 组件的对比:
  • 当对比两个相同类型的 React 组件时,React 会保留 DOM 节点,仅对比并更新变化的属性。
  • 对于组件实例,React 会更新组件的 props,并重新调用 render 方法来决定如何更新。这个过程可能会导致组件的状态变化。
3. 子节点的对比:
  • 当处理同一层级的子节点时,React 会尽可能地重用节点。
  • 使用 key 属性可以帮助 React 确定哪些子元素可以保持不变,哪些需要重新排列或创建。

优化:

React 的 Diff 算法还采用了一些优化策略,如只进行同级别(不跨层级)的对比,这大大减少了对比的复杂度和时间。尽管这可能导致在某些极端情况下的非最优更新路径,但在实践中这种情况很少发生,并且得到的性能提升远远超过了这种潜在的不足。

总的来说,React 的 Diff 算法通过智能的对比策略和假设,减少了不必要的 DOM 更新,从而提高了应用的性能和响应速度。

三 React key

React 中的 key 属性在组件和元素的列表渲染中扮演着至关重要的角色。它的主要目的是帮助 React 识别哪些项发生了变化、添加或者被移除。简而言之,key 提供了元素的唯一标识。

作用和重要性:

  1. 性能优化: 在对比虚拟 DOM 时,key 帮助 React 识别元素是否变更,这使得 React 可以仅更新实际改变了的元素,而不是删除旧的 DOM 元素并创建新的。这种机制极大地提高了应用的性能。

  2. 减少重复渲染: 通过确保每个元素有一个唯一的 key,React 可以避免在数组中重新渲染所有元素。假设你有一个项目列表,只更新了一个项目的内容,如果每个项目都有独一无二的 key,React 将只重新渲染那个被更新的项目,而不是整个列表。

  3. 正确的元素状态管理: 在使用列表和其他可变数据结构时,如果没有 key,React 可能会错误地复用组件状态。这可能会导致难以追踪的错误和不一致的 UI 状态。使用 key 可以保证即使数据项的位置改变,元素状态也能正确地被管理和复用。

使用方法:

在渲染列表时,你应该给每个被渲染的列表项一个唯一的 key 属性:

const todos = [{id: 1, text: 'Do homework'}, {id: 2, text: 'Read book'}];

function TodoList() {
  return (
    <ul>
      {todos.map(todo =>
        <li key={todo.id}>
          {todo.text}
        </li>
      )}
    </ul>
  );
}

在这个例子中,每个 todo 项有一个唯一的 id,这个 id 被用作 key。这样,即使数据变动引起顺序改变,React 也能准确地识别和处理每一个 todo 项,保持高效的渲染性能和正确的组件状态。

注意: 使用数组索引作为 key 是最后的选择,因为如果项目顺序发生变化,索引也会变,这可能导致性能问题或者状态错误。尽可能地使用一个能够唯一标识元素的属性作为 key

key解决的问题

在React等使用虚拟DOM的前端框架中,key 属性主要解决的是识别和追踪列表中各个元素在重新渲染过程中的变化问题。具体来说,key 属性在以下方面发挥着重要作用:

  1. 性能优化: 当组件状态发生变化时,React会重新渲染组件。如果组件渲染的是一个列表,React需要决定哪些列表项被更新、添加或删除。key 帮助React识别列表中每个元素的身份,以便它可以只重新渲染必要的元素,而不是整个列表。这大大提高了应用程序的性能。

  2. 减少渲染时间: 如果没有key,React在更新列表时可能会采取更消极的策略,比如重新渲染整个列表,从而导致不必要的DOM操作,这会降低应用性能。

  3. 维护组件状态: 在动态的列表中,如果列表项的顺序可以改变,没有key或者使用错误的key(如数组索引)可能会导致状态管理问题。例如,如果列表项被重新排序,React可能会错误地将某个组件的状态与另一个组件关联起来。正确的key可以确保组件状态与正确的元素相关联,即使元素的顺序发生了改变。

  4. 识别元素的增加和删除: 在列表操作中,当添加或删除元素时,key可以帮助React快速识别哪些元素是新增的,哪些是被删除的,从而进行针对性的DOM操作,而不是重新渲染整个列表。

总结来说,key的主要作用是提高性能,通过为列表中的每个元素分配一个稳定且独特的标识符,帮助React高效地执行虚拟DOM的diff算法,只更新变化的部分,维护状态的一致性,并减少不必要的DOM操作。因此,在开发过程中,为列表中的每个元素提供唯一的key值是一种最佳实践。

四 虚拟 DOM 的引入与直接操作原生 DOM 相比,哪一个效率更高为什么

虚拟 DOM 的引入通常被认为在大多数现代 Web 应用场景中比直接操作原生 DOM 拥有更高的效率,主要原因包括以下几点:

1. 批量DOM操作和最小化DOM更新

虚拟 DOM 通过在内存中对比前后状态的差异(通过所谓的 diff 算法),来确定真实 DOM 需要进行的最小更新。然后,这些变化会被批量地、一次性应用到真实 DOM 上。这种方法减少了浏览器的重绘(repaint)和重排(reflow)操作,这些操作是影响 Web 应用性能的主要因素。

2. 减少直接DOM操作的开销

直接操作 DOM 是昂贵的,因为每次更改都会直接影响到浏览器的文档对象模型(DOM),这可能会引发一系列的重绘和重排。相比之下,虚拟 DOM 的操作是在 JavaScript 内存中进行的,与直接操作真实 DOM 相比,它能显著减少这种开销。

3. 提高开发效率和可维护性

虚拟 DOM 除了性能优势之外,还通过声明式编程模型来简化了开发过程。开发者可以关注于状态(state)的管理,而不是如何操作 DOM 来反映这些状态的变化。这使得代码更容易理解和维护,并且能够提高开发效率。

但是,也有例外情形

需要注意的是,虚拟 DOM 并非在所有情况下都比直接操作原生 DOM 更高效。例如,在处理极其简单的 DOM 更新时,原生 DOM 操作可能会更快,因为此时虚拟 DOM 的 diff 算法和更新策略所带来的开销可能会超过直接操作原生 DOM 的成本。然而,随着应用规模的增长和复杂度的提升,虚拟 DOM 的优势会变得更加明显。

结论

总的来说,虚拟 DOM 的引入主要是为了提高大规模应用的性能和开发效率。通过批量更新和最小化真实 DOM 操作,它能够提供比直接操作原生 DOM 更好的性能优势,特别是在处理大型或复杂的应用时。然而,对于极其简单的场景,直接操作 DOM 可能会稍微快一些。选择合适的方法取决于具体的应用场景和性能要求。

五 为什么React 与 Vue 的 diff 算法有何不同

React 和 Vue 都使用虚拟 DOM 以及 diff 算法来高效地更新 DOM,但它们在具体实现上有些差异。以下是 React 和 Vue 在 diff 算法上的一些主要区别:

React 的 Diff 算法:

  • 层级对比: React 对虚拟 DOM 树进行层级对比,如果一个组件的类型发生了变化,React 会销毁整个组件下的子树并重新创建。
  • 列表对比: 在对比列表元素时,如果没有明确的key,React 默认采用索引作为每项的 key 值,这可能会导致性能问题和组件状态的错误关联。提供一个独一无二的key可以帮助React更准确、更快地识别元素。
  • 子元素对比: React 默认情况下会逐个对子元素进行对比,这就需要开发者理解并合理使用key以优化性能。

Vue 的 Diff 算法:

  • 双端比较: Vue 的 diff 算法更为复杂。它采用了一种双端比较的策略。当对列表进行对比时,Vue 同时从列表的两端(头部和尾部)开始进行元素的对比,这样可以在一些情况下减少元素的移动次数。
  • 就地更新: 如果没有提供key,Vue 将尽可能就地复用同类型的元素,而不是销毁再重新创建,尽管这样可能会导致一些边缘情况下的问题。
  • 列表对比优化: Vue 在处理列表时进行了一些针对性优化,比如在处理含有相同子元素的静态节点列表时,可以提供更高效的更新。

总的来说,Vue 的 diff 算法在处理列表对比时进行了更多的优化,特别是通过双端对比算法减少不必要的元素移动。然而,不管是 React 或 Vue,合理使用key属性都是至关重要的,因为它们都依赖于key来识别列表中各个元素的身份,以执行高效的更新。开发者应当根据具体情况,为列表中的每个元素提供一个稳定且独一无二的key值,以帮助框架准确快速地进行虚拟 DOM 的 diff 过程。

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

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

相关文章

kafka(四)——生产者流程分析(c++)

前言 kafka生产者负责将数据发布到kafka集群的主题&#xff1b;kafka生产者消息发送方式有两种&#xff1a; 同步发送异步回调发送 流程 流程说明&#xff1a; Kafka Producer整体可看作是一个异步处理操作&#xff1b;消息发送过程中涉及两个线程&#xff1a;main线程和se…

一文读懂RISC-V与ARM

RISC-V和ARM是近年来备受关注的两种处理器架构。RISC-V是一种基于精简指令集计算(RISC)原理的开源指令集架构(ISA)&#xff0c;而ARM是一种专有ISA&#xff0c;由于其长期存在于嵌入式系统和移动设备中&#xff0c;已成为嵌入式系统和移动设备的主导选择。市场以及多年积累的信…

【网络】什么是RPC

RPC 是Remote Procedure Call的缩写&#xff0c;译为远程过程调用。是一个计算机通信协议。 1、为什么需要远程调用 在如何给女朋友解释什么是分布式这一篇文章中介绍过&#xff0c;为了提升饭店的服务能力&#xff0c;饭店从一开始只有一个负责所有事情的厨师发展成有厨师、切…

前端二维码工具小程序产品使用说明书

一、产品概述 前端二维码工具小程序是一款便捷实用的二维码生成与识别工具&#xff0c;通过本小程序&#xff0c;用户可以轻松根据文本或链接生成二维码&#xff0c;并支持扫一扫功能识别二维码内容&#xff0c;同时提供复制识别内容的功能。此外&#xff0c;本小程序还具备美…

如何使用Java和RabbitMQ实现延迟队列(方式二)?

前言 昨天写了一篇关于Java和RabbitMQ使用插件实现延迟队列功能的文章&#xff0c;今天来讲下另外一种方式&#xff0c;不需要RabbitMQ的插件。 前期准备&#xff0c;需要安装好docker、docker-compose的运行环境。 需要安装RabbitMQ的可以看下面这篇文章。 如何使用PHP和R…

React - 请你说一说setState是同步的还是异步的

难度级别:中高级及以上 提问概率:70% 在React项目中,使用setState可以更新状态数据,而不能直接使用为this.state赋值的方式。而为了避免重复更新state数据,React首先将state添加到状态队列中,此时我们可以通过shouldComponentUpdate这个钩…

WWDC24定档6月 | 崩坏3将推Mac系统版 苹果AI启航 visionOS 2.0将系数登场WWDC24

这几天又有一件苹果用户圈大事发生了&#xff01;WWDC24正式定档&#xff0c;将在6月10日-14日召开&#xff0c;届时一众软件系统&#xff0c;包括iOS18&#xff0c;iPadOS&#xff0c;WatchOS&#xff0c;VisionOS等等&#xff0c;都将迎来更新。另外就是手游崩坏3官宣&#x…

vector的使用和底层模拟实现

爱吃喵的鲤鱼 个人主页 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 我们已经 学习了string在来实现vector会发现他们两的结构很像&#xff0c;而string只支持存储字符串&#xff0c;vector支持任意类型&#xff1b; 一、vector是什么…

3. Django 初探路由

3. 初探路由 一个完整的路由包含: 路由地址, 视图函数(或者视图类), 可选变量和路由命名. 本章讲述Django的路由编写规则与使用方法, 内容分为: 路由定义规则, 命名空间与路由命名, 路由的使用方式.3.1 路由定义规则 路由称为URL (Uniform Resource Locator, 统一资源定位符)…

-bash: cd: /etc/hadoop: 没有那个文件或目录

解决办法&#xff1a;source /etc/profile 运行 source /etc/profile 命令会重新加载 /etc/profile 文件中的配置&#xff0c;这样做的目的是使任何更改立即生效&#xff0c;而不需要注销并重新登录用户。通常&#xff0c;/etc/profile 文件包含系统范围的全局 Shell 配置&…

电商社交新零售:创新引领新趋势,变革新零售思维格局-亿发

新零售O2O模式是如何颠覆传统零售商业模式&#xff1f; 传统电商出现瓶颈&#xff1a; 传统电商在发展过程中逐渐出现了瓶颈&#xff0c;主要表现在市场竞争激烈、用户获取成本上升、用户黏性下降等问题。传统电商往往只能通过价格竞争或促销活动来吸引用户&#xff0c;而这种…

hexo接入github Discussions评论系统

评论存储仓 可以是你的博客项目的(github)仓库&#xff0c;也可以单独新建一个评论存储仓库。 我的博客项目在gitee上&#xff0c;就以新建存储仓为例&#xff1a; 使用Discussions评论系统必须开通Discussions模块&#xff01; 安装giscus插件 https://github.com/apps/…

数据仓库发展历史与架构演进

从1990年代Bill Inmon提出数据仓库概念后经过四十多的发展&#xff0c;经历了早期的PC时代、互联网时代、移动互联网时代再到当前的云计算时代&#xff0c;但是数据仓库的构建目标基本没有变化&#xff0c;都是为了支持企业或者用户的决策分析&#xff0c;包括运营报表、企业营…

vscode中vue插件

在Visual Studio Code (VSCode) 中&#xff0c;有许多插件可以帮助Vue开发者提高工作效率和代码质量。以下是一些针对Vue开发的必备VSCode插件&#xff0c;结合了多篇搜索结果中的信息&#xff0c;以提供详尽的介绍。 Volar Volar是Vue.js开发者的官方推荐插件&#xff0c;专门…

python基于opencv实现数籽粒

千粒重是一个重要的农艺性状&#xff0c;通过对其的测量和研究&#xff0c;我们可以更好地理解作物的生长状况&#xff0c;优化农业生产&#xff0c;提高作物产量和品质。但数籽粒数目是一个很繁琐和痛苦的过程&#xff0c;我们现在用一个简单的python程序来数水稻籽粒。代码的…

React之基础项目搭建

前言 React的生态系统非常庞大&#xff0c;拥有大量的第三方库和工具&#xff0c;如React Native&#xff08;用于构建原生移动应用&#xff09;、Next.js&#xff08;用于构建服务器渲染应用&#xff09;、Create React App&#xff08;用于快速搭建React应用的脚手架&#x…

【机器学习300问】62、若想将逻辑回归用于多分类有哪些常见做法?

逻辑回归算法在设计之初是用于二分类问题的&#xff0c;但若想把它用在多分类上也不是不行&#xff0c;这得看你具体面临的多分类问题是什么样的&#xff08;问题的定义&#xff09;。不同的问题就有不同的应对之策&#xff1a; 一、一对一 &#xff08;1&#xff09;方法的原…

【JavaEE】_Spring MVC项目获取Header

目录 1. 使用Servlet原生方法获取Header 2. 使用Spring注解获取Header 1. 使用Servlet原生方法获取Header .java文件内容如下&#xff1a; package com.example.demo.controller;import com.example.demo.Person; import org.springframework.web.bind.annotation.*; impor…

Linux-等待子进程

参考资料&#xff1a;《Linux环境编程&#xff1a;从应用到内核》 僵尸进程 进程退出时会进行内核清理&#xff0c;基本就是释放进程所有的资源&#xff0c;这些资源包括内存资源、文件资源、信号量资源、共享内存资源&#xff0c;或者引用计数减一&#xff0c;或者彻底释放。…

14届蓝桥杯 C/C++ B组 T5 接龙排序 (最长上升子序列DP+优化)

不难发现这是一个LIS问题&#xff0c;但是如果直接套用LIS的模版&#xff0c;在数据范围到达 1 e 5 1e5 1e5 的情况下&#xff0c;就只能够得到一半的分数&#xff0c;所以我们需要对其进行优化。 首先给出暴力的代码&#xff1a; #include<iostream> using namespace…