虚拟DOM最核心的部分是patch,它可以将vnode渲染成真实DOM.
patch也可以叫做patching算法,通过它渲染真实DOM时,并不会暴力覆盖原有DOM.而是比对新旧俩个vnode之间有哪些不同,然后根据对比结果找出需要更新的节点进行更新.这点从名字就可以看出,patch本身就有补丁,修补等意思.其实际作用是在现有DOM上进行修改来实现更新视图的目的.
之所以要这么做,主要是因为DOM操作的执行速度远不如JavaScript的运算速度快。因此,把大量的DOM操作搬运到JavaScript 中,使用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提升性能。这本质上其实是使用JavaScript 的运算成本来替换DOM操作的执行成本,而JavaSeript的运算速度要比DOM快很多、这样做很划算,所以才会有虚拟DOM。

1.patch介绍

对比两个vnode之间的差异只是patch的一部分, 这是手段,而不是目的。patch 的目的其实是修改DOM节点,也可以理解为渲染视图。上面说过,patch 不是暴力替换节点,而是在现有DOM上进行修改来达到谊染视图的目的。对现有DOM进行修改需要做三件事:

  • 创建新增的节点;
  • 删除已经废弃的节点;
  • 修改需要更新的节点。

我们知道patch的过程其实就是创建节点、删除节点和修改节点的过程,接下来主要讨论在什么情况下创建新节点,插入到什么位置;在什么情况下删除节点,删除哪个节点;在什么情况下修改节点,修改哪个节点等。在详细讨论什么情况下需要对节点进行更改之前,我们需要先弄清楚一个问题。
事实上,我一再强调:之所以需要通过算法来比对两个节点之间的差异,并针对不同的节点进行更新,主要是为了性能考虑。我们完全可以把整个旧节点从DOM中删除,然后使用最新的状态(state)重新生成一份全新的节点并插入到DOM中,这种方式完全可以实现功能。由于我们的最终目的是渲染视图,所以可以发现渲染视图的标准是以vnode (使用最新状态创建的vnode )来渲染而不是oldVnode (上一次渲染DOM所创建的vnode )。也就是说,当oldVnode和vnode不一样的时候, 以vnode为准来渲染视图。但是这样性能会很低.
那么对比之后如果不一致,会有以下操作:
1.什么情况会新增节点(如何新增下面会详细介绍)
本节中,我们主要讨论在什么情况下新增节点。之所以讨论什么情况下需要新增节点,本质上是为了使用JavaScript的计算成本来换取DOM的操作成本。如果一个节点已经存在于DOM中,那就不需要重新创建一个同样的节点去替换已经存在的节点。事实上,只有那些因为状态的改变而新增的节点在DOM中并不存在时,我们才需要创建一一个节点并插人到DOM中。首先,新增节点的一个很明显的场景就是,当oldVnode不存在而vnode存在时,就需要使用vnode生成真实的DOM元素并将其插人到视图当中去。这通常会发生在场景一:首次渲染中。因为首次渲染时,DOM中不存在任何节点,所以oldVnode是不存在的。
图7-1给出了当oldVnode不存在时,直接使用vnode创建元素并谊染视图。

图7-2给出了首次渲染视图时(页面中没有任何节点,oldVnode并不存在),只需要使用vnode即可。
除了,上面介绍的情况需要新增节点之外,还有一种情况也需要新增节点。
场景二:当vnode和oldVnode完全不是同一个节点时,需要使用vnode生成真实的DOM元素并将其插入到视图当中
前面介绍过,当oldVnode和vnode 不一样的时候, 以vnode 为标准来渲染视图。因此,当vnode和oldVnode完全不是同一个节点的时候,可以得知vnode就是一个全新的节点,而oldVnode就是一个被废弃的节点。

这种情况下,我们要做的事情就是使用vnode创建一个新 DOM节点,用它去替换oldVnode所对应的真实DOM节点,如图7-3所示。

2.什么情况下会删除节点(如何删除下面会详细介绍)
删除节点的场景上一节略有提及,就是当一个节点只在oldVnode中存在时,我们需要把它从DOM中删除。因为渲染视图时,需要以vnode为标准,所以vnode中不存在的节点都属于被废弃的节点,而被废弃的节点需要从DOM中删除。如图7-3所示,当oldVnode和vnode完全不是同一个节点时,在DOM中需要使用vnode创建的新节点替换oldVnode所对应的旧节点,而替换过程是将新创建的DOM节点插人到旧节点的旁边,然后再将旧节点删除,从而完成替换过程。
3.什么情况会更新节点(如何更新下面会详细介绍)
前面介绍了新增节点和删除节点的场景,我们发现它们之间有一个共同点,那就是两个虚拟节点是完全不同的。由于我们需要以新节点为标准谊染视图,所以这个时候只有两种操作可以执行:将旧节点删除或者创建新增节点。其实除了前面介绍的场景外,另一个更常见的场景是新旧两个节点是同一个节点。 当新旧两个节点是相同的节点时,我们需要对这两个节点进行比较细致的比对,然后对oldVnode 在视图中所对应的真实节点进行更新。
举个简单的例子,当新旧两个节点是同一个文本节点,但是两个节点的文本不一样时, 我们需要重新设置oldVnode在视图中所对应的真实DOM节点的文本。
图7-4给出了用vnode中的文字替换DOM中文字的过程。视图中的文本节点所包含的文字是“我是文字",而当状态发生变化时,将文本改成了“我是文字2”. 这时使用改变后的状态生成了新的vnode,然后将vnode与oldVnode进行比对,发现它们是同一个节点,再将这两个节点进行更详细的比对,比对结果是文字发生了变化,最后将真实DOM节点中的文本改成了vnode中的文字“我是文字2”。

这里提到的俩个相同节点进行更详细的比对,这个比对会在之后详细介绍.

2.小结

通过前面的介绍,可以发现整个patch 的过程并不复杂。当oldVnode 不存在时,直接使用vnode渲染视图;当oldVnode和vnode都存在但并不是同一个节点时,使用vnode创建的DOM元素替换旧的DOM元素;当oldVnode和vnode是同一个节点时,使用更详细的对比操作对真实的DOM节点进行更新。

3.创建节点过程的详细介绍

我们之前介绍了什么情况下会创建元素并将元素渲染到视图,在这里我们将介绍一个元素从创建到渲染的过程。
前面学习中,我们知道创建一个真实DOM元素所需的信息都保存在vnode中,我们通过vnode来创建一个真实的DOM元素,vnode有类型,所以在创建DOM元素时,最重要的是根据vnode的类型来创建出相同类型的DOM元素,然后将DOM元素插入到视图。
然而事实上,只有三种类型的节点会被创建并插入到DOM中:①元素节点 ②注释节点 ③文本节点
①判断vnode是否是元素节点以及创建:
判断是否为元素节点,只需要判断它是否具有tag属性即可,如果有则是元素节点。接着,我们就可以调用当前环境下的createElement方法(浏览器环境下就是document.createElement)来创建真实的元素节点。当该元素节点被创建后,接下来就是把它插入到指定的父节点中。
②判断vnode是否是注释节点以及创建
在创建节点时,如果vnode中不存在tag属性,那么它可能会是另外俩种节点:注释节点和文本节点。
注释节点有唯一标识的属性isComment,在所有类型的vnode中,只有注释节点的isComment的属性是true。注释节点创建调用当前环境下的createComment方法(浏览器环境调用document.createTextNode)来创建注释节点并将其插入到指定的父节点中。
③判断vnode是否是文本节点以及创建
如果是文本节点,调用当前环境下的createTextNode方法(浏览器环境下调用document.createTextNode)来创建真实的文本节点并将其插入到指定父节点中。
如何将元素渲染到视图呢?
只需要调用当前环境下的appendChild方法(浏览器下调用parentNode.appendChild),就可以将一个元素插入到指定的父节点中。如果指定父节点已经被渲染到视图,则把元素插入到指定父节点下面,将会自动将元素渲染到视图。
其实创建元素节点还缺了一个步骤:元素节点通常还会有子节点(chiildren),所以一个元素被创建后,需要将它的子节点也创建出来并插入到这个刚创建出来的节点下面。
创建子节点的过程是一个递归过程。 vnode中的children属性保存了当前节点的所有子虚拟节点( child virtual node ),所以只需要将vnode中的children属性循环一遍, 将每个子虚拟节点都执行一遍创建元素的逻辑,就可以实现我们想要的功能。创建子节点时,子节点的父节点就是当前刚创建出来的这个节点,所以子节点被创建后,会被插人到当前节点的下面。当所有子节点都创建完并插人到当前节点中之后,我们把当前节点插入到指定父节点的下面。如果这个指定的父节点已经被渲染到视图中,那么将当前这个节点插入进去之后,会将当前节点(包括其子节点)渲染到视图中。


4.删除节点过程的详细介绍

简单来说,上面代码实现的功能是删除vnodes数组中从startIdx指定的位置到endIdx指定位置的内容。

removeNode用于删除视图中的单个节点,而removeVnodes用于删除一组指定的节点。

上面代码的逻辑是将当前元素从它的父节点中删除,其中nodeops是对节点操作的封装。
有同学可能会对nodeOps感到奇怪,为什么不直接使用parent. removeChild(child)删除节点,而是将这个节点操作封装成函数放在nodeOps里呢?
其实这涉及跨平台谊染的知识,我们知道阿里开发的Weex可以让我们使用相同的组件模型为ios和Android编写原生渲染的应用。也就是说,我们写的Vue,js组件可以分别在ios和Android环境中进行原生渲染。而跨平台渲染的本质是在设计框架的时候,要让框架的渲染机制和DOM解耦。只要把框架更新DOM时的节点操作进行封装,就可以实现跨平台渲染,在不同平台下调用节点的操作。换言之,如果我们把这些平台下节点操作的封装看成渲染引擎,那么将这些渲染引擎所提供的节点操作的API和框架的运行时对接一下,就可以实现将框架中的代码进行原生渲染的目的。这就是将removeChild方法封装到nodeOps中的原因。更多关于跨平台渲染的内容已超出本章的讨论范围,这里不再展开讨论。

5.更新节点过程的详细介绍

我们之前介绍了只有两个节点是同一个节点时,才需要更新元素节点,而更新节点并不是很暴力地使用新节点覆盖旧节点,而是通过比对找出新旧两个节点不一样的地方, 针对那些不一样的地方进行更新。本节中,我们将介绍节点更新的详细过程。
①判断是否是静态节点
在更新节点时,首先需要判断新旧两个虚拟节点是否是静态节点,如果是,就不需要进行更新操作,可以直接跳过更新节点的过程。
那么,什么是静态节点?
静态节点指的是那些一旦渲染到界面 上之后,无论日后状态如何变化,都不会发生任何变化的节点。
例如:<p>我是静态节点,我不需要发生变化</p>
上面的html就是一个静态节点,它不会因为状态变化而变化,这个节点一旦被渲染到视图,无论状态是否发生变化,都不会影响到这个节点,这个节点就永远都不需要重新渲染。
②新虚拟节点有文本属性
当新旧两个虚拟节点( vnode和 oldVnode)不是静态节点,并且有不同的属性时,要以新虚拟节点( vnode)为准来更新视图。根据新节点(vnode)是否有text属性,更新节点可以分为两种不同的情况。
如果新生成的虚拟节点( vnode)有text属性,那么不论之前旧节点的子节点是什么,直接调用setTextContent方法(在浏览器环境下是 node.textContent方法)来将视图中 DOM节点的内容改为虚拟节点( vnode )的text属性所保存的文字。
因为更新是以新创建的虚拟节点 ( vnode)为准的,所以如果新创建的虚拟节点有文本,那么根本就不需要关心之前旧节点中所包含的内容是什么,无论是文本还是元素节点,这都不重要。唯一需要关心的是,如果之前的旧节点也是文本,并且和新节点的文本相同,那么就不需要执行setTextContent方法来重复设置相同的文本。
简单来说,就是当新虚拟节点有文本属性,并且和旧虚拟节点的文本属性不一样时,我们可以直接把视图中的真实DOM节点的内容改成新虚拟节点的文本。
③新虚拟节点无文本属性
如果新创建的虚拟节点没有text属性,那么它就是一个元素节点。元素节点通常会有子节点,也就是children属性,但也有可能没有子节点,所以存在两种不同的情况。
1.有children的情况
当新创建的虚拟节点有 children属性时,其实还会有两种情况,那就是要看旧虚拟节点( oldVnode)是否有children属性。
如果旧虚拟节点也有children属性,那么我们要对新旧两个虚拟节点的children进行一个更详细的对比并更新。更新children可能会移动某个子节点的位置,也有可能会删除或新增某个子节点,具体更新children的过程我们会在之后的内容中详细介绍。
如果旧虚拟节点没有children属性,那么说明旧虚拟节点要么是一个空标签,要么是有文本的文本节点。如果是文本节点,那么先把文本清空让它变成空标签,然后将新虚拟节点( vnode )中的children挨个创建成真实的DOM元素节点并将其插入到视图中的DOM节点下面。
2.无 children的情况
当新创建的虚拟节点既没有text属性也没有 children 属性时,这说明这个新创建的节点是一个空节点,它下面既没有文本也没有子节点,这时如果旧虚拟节点( oldVnode )中有子节点就删除子节点,有文本就删除文本。有什么删什么,最后达到视图中是空标签的目的。

6.小结

本节重点讨论了更新节点的详细过程以及处理逻辑,讨论的内容包括新虚拟节点有文本时如何处理,有children属性时如何处理,以及没有children属性时怎么处理等。图7-9给出了更新节点的整体逻辑。

在源码中,真实的实现过程是如图7-10所示:

Patch:虚拟DOM最核心的部分--如何对比虚拟DOM树,以及如果生成真实DOM相关推荐

  1. 虚拟DOM的实现原理和优劣对比

    虚拟DOM的实现原理和优劣对比 Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化. 虚拟DOM就是为了解决浏览器性能问题而被设计出来的.如 ...

  2. 真实DOM和虚拟DOM

    文章目录 如何高效操作DOM 什么是DOM 浏览器真实解析DOM的流程 为什么说操作DOM耗时 如何高效操作DOM 虚拟DOM 什么是虚拟DOM 为什么要有虚拟DOM 虚拟DOM的作用 Vue中的虚拟 ...

  3. Vue 原理解析(五)之 虚拟Dom 到真实Dom的转换过程

    上一篇 vue 原理解析(四): 虚拟Dom 是怎么生成的 再有一颗树形结构的Javascript对象后, 我们需要做的就是讲这棵树跟真实Dom树形成映射关系.我们先回顾之前的mountComponn ...

  4. React--虚拟DOM和真实DOM

    学习资源推荐 真实dom <div className="foo"><h1>Hello React</h1> </div> 虚拟do ...

  5. vue核心之虚拟DOM(vdom)与真实DOM页面渲染过程

    一.真实DOM和其解析流程? 浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树--创建StyleRules--创建Render树--布局Layout--绘制Painting 第一步,用HTM ...

  6. jsx后缀的是什么文件_React核心特性-从JSX到虚拟DOM

    JSX 简单来讲, React 为方便 View 层组件化,承载了构建 HTML 结构化页面的职责. JSX代码既可以写在.jsx后缀的文件或传统的js文件中,现在随着微软TypeScript的普及, ...

  7. 从VirtualDom(虚拟Dom)到真实DOM

    浏览器中的Dom更新 在浏览器中渲染引擎将 node 节点添加到 另外节点中时会触发样式计算.布局.绘制.栅格化.合成等任务,这一过程称为重排. 除了重排之外,还有可能引起重绘或者合成操作,也就是&q ...

  8. React 虚拟Dom 转成 真实Dom 实现原理

    React 和 React-Dom 是核心模块 React:是核心库,当使用JSX语法时,必须让React 存在当前作用域下 React元素:是通过JSX语法创建的在JS中存在的HTML的标签 JSX ...

  9. virtual DOM和真实DOM的区别_让虚拟DOM和DOMdiff不再成为你的绊脚石

    来源 | https://juejin.im/post/5c8e5e4951882545c109ae9c Keep Moving 时至今日,前端对于知识的考量是越来越有水平了,逼格高大上了 各类框架大 ...

最新文章

  1. 阿里员工绩效只拿3.25!自我反省:平时假装努力!晚上没加班!去厕所时间太长!还老买彩票!...
  2. NBT-19年2月刊4篇35分文章聚焦宏基因组研究
  3. python程序如何做界面_python是如何写界面程序的?
  4. Windows上也能用Swift编程了,官方编译工具安装包现已上线
  5. Office 2016 for Mac试用之Excel篇
  6. .NET全栈开发工程师学习路径
  7. 【Machine Learning】决策树之ID3算法 (2)
  8. 研读代码必须掌握的五个Eclipse快捷键
  9. C#中MSMQ消息队列测试疑问
  10. xp电脑怎么进入bios
  11. 《算法竞赛进阶指南》数论篇
  12. zouxy09博客原创性博文导航
  13. java平台无关性_为什么Java能够实现平台无关性?
  14. maven 强制更新_maven入坑指南
  15. linux串口环形缓冲区,能不能讲解下串口环形缓冲区的概念?
  16. java 判断5张牌的组成
  17. 环境工程学期末复习完整版
  18. 服务端软件安全测评标准及实施指南 V1.0
  19. 搭建公司内部论坛 只需简单三步 1 (安装Discuz)
  20. 错误处理 宏 c语言,C语言零基础教程之预处理和宏定义篇

热门文章

  1. PostgreSQL数据库动态共享内存管理器——dynamic shared memory segment
  2. 【ZZULIOJ】1026: 字符类型判断
  3. 德国计算机课程匹配度,匹配度对于德国留学有多重要
  4. Java中Lambda表达式和stream的使用
  5. 【烈日炎炎战后端】JAVA虚拟机(3.6万字)
  6. 地球空间数据交换格式
  7. OpenCV读取图片和保存图片全黑
  8. 水仙花数(调用函数)
  9. 制售《原神》外挂非法获利200万,外挂的危害有多大?
  10. 初识:神经网络(Neural Networks)