Web前端学习之虚拟DOM如何进化为真实DOM

news2024/11/17 0:06:00

Vue和React的Render函数中都涉及到了Virtual DOM的概念,Virtual DOM也是性能优化上的重要一环,同时突破了直接操作真实DOM的瓶颈,本文带着以下几个问题来阐述Virtual DOM。

1.为什么要操作虚拟 DOM?

2.什么是虚拟 DOM?

3.手把手教你实现虚拟 DOM 渲染真实 DOM

希望阅读本文之后,能够让你深入的了解虚拟 DOM并且在开发和面试中收益。

为什么要操作虚拟 DOM

为了帮助我们更好的理解为什么要操作虚拟 DOM,我们先从浏览器渲染[1]一个 HTML 文件需要做哪些事情说起:

IMG_256

浏览器渲染机制大致可以分为以下 5 步走:

1.创建 DOM tree

2.创建 Style Rules

3.构建 Render tree

4.布局 Layout

5.绘制 Painting

我们过去使用原生JavaScript和jquery去操作真实DOM的时候,浏览器会从构建 DOM� 开始从头到尾的执行一遍渲染的流程。

在一次开发中,假如产品告诉你一个需求,你需要在一次操作中更新10个DOM节点,理想状态是浏览器一次性构建完DOM树,再执行后续操作。但浏览器没这么智能,收到第一个更新 DOM 请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行 10 次流程。

过了一会产品经理把你叫过去和你说把需求改一下,此时你又需要操作一次 DOM 的更新,那么这个时候之前做的 10 次 DOM 操作就是白白浪费性能,浪费感情。

即使计算机硬件一直在更新迭代,但是操作DOM的代价仍旧是昂贵的,频繁操作 DOM 还是会出现页面卡顿,影响用户的体验。真实的 DOM 节点,哪怕一个最简单的 div 也包含着很多属性,可以打印出来直观感受一下:
IMG_257
如此多的属性,如果每次对 DOM 结构都进行更新,一次,两次,三次…一百次…一千次…,可想而知,是多么庞大的数据量。

因此虚拟DOM就是为了解决这个浏览器性能问题而被设计出来的。例如前面的例子,假如一次操作中有 10 次更新 DOM 的动作,虚拟DOM不会立即操作DOM,而是将这 10 次更新 DOM 的动作通过Diff算法最终生成一个js对象,然后通知浏览器去执行一次绘制工作,这样可以避免大量的无谓的计算量。

什么是虚拟 DOM

虚拟 DOM[2]就是我们上面所说的js对象。

其本质上就是在JS和DOM之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM),直接操作内存中的 JS 对象的速度显然要更快。

function vnode(tag, data, key, children, text) {

return {

    tag,

    data,

    key,

    children,

    text

}

}

举个栗子:

假如我们有这样的一个 DOM 树

    • 前端简报
    • vue
    • 那么,我们怎么用 js 的对象来对应到这个树呢?

      {

      tag: 'ul',        // 元素标签
      
      data: {           // 属性
      
          class: 'list'
      
      },
      
      key: '',
      
      text: '',  // 文本内容
      
      children: [
      
          {
      
              tag: "li",
      
              data: {
      
                  class: "item"
      
              },
      
              key: '',
      
              text: '',
      
              children: [
      
                  {
      
                      tag: undefined,
      
                      data: undefined,
      
                      key: undefined,
      
                      text: '前端简报',
      
                      children: []
      
                  }
      
              ]
      
          },
      
          {
      
              tag: "li",
      
              data: "",
      
              key: '',
      
              text: '',
      
              children: [
      
                  {
      
                      tag: undefined,
      
                      data: undefined,
      
                      key: undefined,
      
                      text: 'vue',
      
                      children: []
      
                  }
      
              ]
      
          }
      
      ]       // 子元素
      

      }

      由此可知:DOM tree的信息都可以用JavaScript对象来表示,反过来,我们也可以用 JavaScript对象表示的树结构来构建一棵真正的DOM树。

      实现虚拟 DOM 渲染真实 DOM

      有了JavaScript对象之后如何转化为真实的 DOM 树结构呢?

      ul 和 li 在js 对象中,页面上并没有此结构,所以我们需要把ul和li转化为和

      标签

      而文本标签我们定义 Vnode 为:

      {

      tag: undefined,

      data: undefined,

      key: undefined,

      text: ‘vue’,

      children: []

      }

      故可以判断tag的类型来确定创建元素的类型.

      function createElm(vnode) {

      let { tag, data, children, key, text } = vnode;
      
      if (typeof tag == "string") {
      
          vnode.el = document.createElement(tag);  //创建元素放到vnode.el上
      
          children.forEach(child => {
      
              vnode.el.appendChild(createElm(child))
      
          })
      
      } else {
      
          vnode.el = document.createTextNode(text);  //创建文本
      
      }
      
      return vnode.el
      

      }

      如果子节点存在并且也是虚拟DOM的话,我们通过递归调用创建子节点。
      IMG_258
      创建 DOM 树结构之后我们需要设置节点的属性,即处理虚拟 DOM 中的data属性。

      function updateProperties(vnode) {

      let el = vnode.el;
      
      let newProps = vnode.data || {};
      
      for (let key in newProps) {
      
          if (key == "style") {
      
              for (let styleName in newProps.style) {
      
                  el.style[styleName] = newProps.style[styleName];
      
              }
      
          } else if (key == "class") {
      
              el.className = newProps.class;
      
          } else {
      
              el.setAttribute(key, newProps[key]);
      
          }
      
      }
      

      }

      在我们创建元素标签之后调用updateProperties方法即可
      IMG_259
      把上面创建出来的真实 DOM 结构 vnode.el 添加到文档当中即可呈现出我们需要的真实 DOM 结构

      let parentElm = document.getElementById(“app”).parentNode; 获取之前app的父亲body

      parentElm.insertBefore(createElm(vnode), document.getElementById(“app”).nextSibling); //body里在老的app后面插入真实dom

      parentElm.removeChild(document.getElementById(“app”)); //删除老的节点
      IMG_260
      总结

      以上就是本文的全部内容,我想我们现在应该了解什么是虚拟DOM的概念了以及虚拟DOM是如何实现真实DOM渲染的。其中用到了主要用到了子节点的递归。

      文章来源:网络 版权归原作者所有

      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理

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

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

    相关文章

    Word内容解析之图表数据获取

    最近遇到一个问题,Word里有个从Excel直接复制进去的图,但那个Excel已经找不到了,无法通过编辑数据获取到表格的数据。这个其实可以用getdata等软件获取,或者鼠标点在表上的点就可以显示数据,再把数据录下来&#xff0c…

    更加灵活、经济、高效的训练 — 新一代搜推广稀疏大模型训练范式GBA

    作者:苏文博、张远行 近日,阿里巴巴在国际顶级机器学习会议NeurIPS 2022上发表了新的自研训练模式 Gloabl Batch gradients Aggregation(GBA,论文链接:https://arxiv.org/abs/2205.11048),由阿里…

    模拟电子技术(七)波形的发生和信号的转换

    (七)波形的发生和信号的转换正弦波振荡电路RC正弦波振荡电路LC正弦波振荡电路正弦波振荡例题电压比较器单限比较器过零比较器一般单限比较器滞回比较器窗口比较器电压比较器例题非正弦波发生电路矩形波发生电路三角波发生电路锯齿波发生电路信号转换电路…

    Visual Studio 调试无法启动调试,拒绝访问

    方法一 win更新了不兼容 ,卸载更新。 1、单击开始菜单,选择【设置】如下图; 2、然后再进入【更新和安全】选项,如下图; 3、查看已安装更新历史记录,如下图红圈 4、这个页面详细列出了最新的更新&#xf…

    绿盟SecXOps安全智能分析技术白皮书 安全分析模型核心服务部署

    安全分析模型核心服务部署 ModelOps 对所有的人工智能 模型(图形模型、语言模型、基于规则的模型)以及决策模型的整个生命周期 进行管理,确保对生产中的所有模型进行独立验证和问责,其核心功能涵盖了模型存储、模型测试、模型回滚…

    28. 如何使用 SAP OData 服务向 ABAP 服务器上传文件

    文章目录 1. 创建对应的自定义数据库表和 ABAP DDIC 结构2. 完成 SEGW 事物码里模型的增强3. 完成必要的 ABAP 编码本教程到目前为止开发的 OData 图书管理服务,可以在 ABAP 系统里对图书数据进行增删改查。 本步骤我们继续介绍如何通过 SAP OData 服务,实现向 ABAP 系统上传…

    0.96寸OLED显示屏介绍

    OLED显示屏简介 OLED,即有机发光二极管(Organic Light Emitting Diode)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性&#x…

    【C语言】常见字符函数和字符串函数

    1.1strlen size_t strlen(const char* str); 字符串已经\0作为结束标志,strlen函数返回的是在字符串中\0前面出现的字符个数(不包含\0)。 参数指向的字符串必须以\0结束。 注意函数的返回值为size_t,是无符号整形(…

    五、Vector底层源码详解

    文章目录特点底层源码分析有参构造器public Vector(int initialCapacity, int capacityIncrement)有参构造器public Vector(int initialCapacity)有参构造器public Vector(Collection<? extends E> c)无参构造器public Vector()扩容机制特点 底层是elementDate数组线程…

    自学Python真的可以吗?

    自学当然可以学成功python了&#xff0c;但是前提是你需要认真去学&#xff0c;而不是三天打渔两天晒网的&#xff0c;因为python初学很容易&#xff0c;稍微过几天忘记也很容易&#xff0c;所以一定要坚持学习&#xff0c;并且通过平时多加练习来熟练掌握各个知识点。 一、学…

    非零基础自学Golang 第15章 Go命令行工具 15.4 注释文档(doc)

    非零基础自学Golang 文章目录非零基础自学Golang第15章 Go命令行工具15.4 注释文档(doc)第15章 Go命令行工具 15.4 注释文档(doc) Go语言文档工具go doc和go fmt一样&#xff0c;也是对godoc的简单封装。 我们通常使用go doc查看指定包的文档。 例如我们查看函数fmt.Println…

    焕然一新的 Vue 3 中文文档要来了

    前言 大家好&#xff0c;我是LBJ&#xff0c;最近参与了 Vue 3新文档的翻译和校验工作 (vuejs/docs contributor 和 docs-zh-cn contributor) 我们知道 Vue 3 新文档 ( vuejs.org ) 已经发布一个多月了&#xff0c;但那是英文版的&#xff0c;不知道你看了没&#xff1f; 没…

    恒业微晶冲刺深交所:年营收4.3亿 戴联平控制73.8%表决权

    雷递网 雷建平 12月20日上海恒业微晶材料科技股份有限公司&#xff08;简称&#xff1a;“恒业微晶”&#xff09;日前递交招股书&#xff0c;准备在深交所创业板上市。恒业微晶计划募资8亿元&#xff0c;用于恒业新型分子筛项目。年营收4.31亿恒业微晶创建于1992年&#xff0c…

    Vue3 Proxy代理为什么要用 Reflect映射

    瞅一眼Vue3源码 地址&#xff1a;https://github.com/vuejs/core/blob/main/packages/reactivity/src/baseHandlers.ts 可以看到Proxy响应式代理 依赖 createGetter与createSetter方法&#xff1a; &#x1f6a5; createGetter function createGetter(isReadonly false, s…

    Go并发大坑:inconsistent mutex state 解决及心得体会

    文章目录前言首次排查&#xff08;未重视&#xff09;问题逐渐严重加大排查力度增加锁日志race detector一次意外的复现写在最后解决问题所必需的品格前言 大概在这篇文章发布数个月之前&#xff0c;某天&#xff0c;线上稳定运行的Go服务突然毫无征兆的发生了一次重启&#x…

    MyBatis的相应API与传统和代理开发的Dao层实现

    MyBatis的相应API 1、SqlSession工厂构建器SqlSessionFactoryBuilder 常用API&#xff1a;SqlSessionFactory build(InputStream inputStream) 通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象 其中&#xff0c;mybatis-config.xml是类加载器的路径&…

    为什么要学编程?为什么是Python?怎么学?

    今天我们聊了为什么要去学编程&#xff0c;学编程为什么要选择Python。上大学的时候我学的信息管理&#xff0c;毕业后选择了和网络相关的网络安全公司做售后。 从上学的时候自己就想过去学学编程&#xff0c;感觉他们敲代码都敲的那么酷&#xff0c;看着满屏的代码还有命令行…

    2022广航蓝桥杯选拔赛压轴题:取数博弈游戏

    原题链接 对于这种「判断先手后手的必胜必败」的题目&#xff0c;博弈论方向是一个优先考虑的方向。 博弈论的重要思想就是决策者都要做出全局最优的决策而非局部最优&#xff0c;就好比专业的棋手走一步看五步&#xff0c;而博弈论则要求棋手走一步看无限步。 解题思路 凡…

    [洛谷]P3613 【深基15.例2】寄包柜

    [洛谷]P3613 【深基15.例2】寄包柜一、问题描述题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示二、思路分析1、算法标签2、算法思路三、代码实现一、问题描述 [洛谷]P3613 【深基15.例2】寄包柜 题目描述 超市里有 n(1≤n≤105)n(1\le n\le10^5)n(1≤n≤105) 个…

    《Quarkus实战》总结

    《Quarkus实战》总结 目录 一、优势二、搭建脚手架三、Rest 1)启用跨源资源共享2)拦截HTTP请求3)使用SSL进行安全连接 四、配置 1)以程序化的方式访问配置属性2)在外部覆盖配置值3)修改日志配置 五、编程模型 1)校验输入值和输出值2)全局异常处理3)创建自定义校验4)以程序化的方…