The Inner Workings Of Virtual DOM 虚拟DOM的内部工作机制

原文地址:https://medium.com/@rajaraodv/the-inner-workings-of-virtual-dom-666ee7ad47cf

虚拟DOM(Virtual DOM,简称VDOM aka VNode)是一种魔法。React、Preact和同类JS库在内核中都使用它。

这篇文章讲述它是如何工作的,大体内容如下:

  1. Babel and JSX
  2. Creating VNode — A single virtual DOM element
  3. Dealing with components and sub-components
  4. Initial rendering and creating a DOM element
  5. Re-rendering
  6. Removing DOM element.
  7. Replacing a DOM element.

零、前言

示例app是一个可过滤的搜索app,包含“过滤清单”和“列表”两部分。

Live app: http://codepen.io/rajaraodv/pen/BQxmjj

大图景:

在高层面上,我们用JSX(html in JS)来写组件,然后它们将会被CLI工具Babel转换为纯JS,然后Preact的超文本功能会将纯JS转为VDOM,并最终经由Preact的虚拟DOM算法,从VDOM中创造出真正的DOM,进而创造出我们的app。

一、Babel And JSX

Preact的一段语句长这样:

JSX允许我们在JS里写html,同时允许我们在html里用{ }写JS。最终JSX把我们的组件变成这样:

JSX虽然很酷,但并不是有效的JS,因为我们需要真实的DOM。所以我们需要转换成相应的JSON对象(VDOM,也是一棵树),这样我们最终可以将它用作创建真实DOM的输入。我们需要一个功能来做到这一点。这个功能在Preact里就是 “h” function,在React就是“React.createElement”。

但是如何将JSX转换为“h”函数调用?这就是Babel出场的地方,它将每一个经历的JSX节点转换成“h”函数调用。

默认情况下,Babel将JSX转换为React.createElement调用,因为它默认为React。

但是我们可以通过添加“Babel Pragma”轻松地将函数的名称更改为我们想要的任何内容(如Preact的“h”),如下所示:

Option 1:
//.babelrc
{ "plugins": [
["transform-react-jsx", { "pragma": "h" }]
]
}
Option 2:
//Add the below comment as the 1st line in every JSX file
/** @jsx h */

“h”函数的输出如下:

{
"nodeName": "",
"attributes": {},
"children": []
}

二、虚拟DOM的算法

1.初始化app

1.1 对给定组件创建虚拟DOM

高两部分展示了初始循环时对给定组件创造虚拟DOM的过程。请注意,它并没有给子组件创造虚拟DOM,那是另一个循环才做的事。

下图展示了App初次加载时发生了什么:

请注意,我们同样称此为“componentWillMount” 和 “render”的生命周期方法(即上图绿色方框)。

此时,我们的虚拟节点里有一个div父节点,它包含“input”和“list”子节点。

1.2 如果不是一个组件,则创建一个真实DOM

这一步,它仅仅对父节点(div)创建了一个真实节点,并对其子节点重复这一过程。

此时,我们的div如下图所示:

1.3 对所有子节点进行重复操作

此时,它对所有子节点(“input”和“list”项)进行循环。

1.4 处理子节点并添加至父节点中

在这一步中,我们将处理叶子。由于“input”具有父(“div”),因此我们将输入作为子项附加到div。然后控件停止并返回以创建“List”(这是“div”的第二个子节点)。

此时,我们的app如下图所示:

1.5 处理子组件

控制返回到步骤1.1并重新开始“List”组件。但由于“List”是一个组件,它调用“List”组件的render方法来获取如下所示的新VNode集。

该循环为List组件完成,并返回List的VNode,如下所示:

1.6 对所有的子组件重复1.1-1.4步

它将为每个节点再次重复上述步骤。到达叶节点后,它会将其附加到节点的父节点并重复该过程。

下图展示了每个节点是如何被加上的(深度优先):

1..7 处理完成

此时,它完成了处理。它只是为所有组件调用“componentDidMount”(从子组件到父组件)并停止。

场景二:删除叶节点

假设我们键入“cal”并按Enter键。这将删除第二个列表节点,叶节点(纽约),同时保留所有其他父节点。

2.1 像之前那样创建虚拟节点

在初始渲染之后,未来的每个变化都是“更新”。在创建VNode时,更新周期与创建周期非常相似,并且会再次创建VNode。

但由于它是组件的更新(而不是创建),它使“componentWillReceiveProps”,“shouldComponentUpdate”和“componentWillUpdate”调用每个组件和子组件。

此外,更新周期,如果那些元素已经存在,则不会重新创建DOM元素。

2.2使用引用真实DOM节点并避免创建重复节点

如前所述,每个组件都引用了在初始加载期间创建的相应真实DOM树。下图显示了此时引用如何查找我们的应用程序。

当创建VNode时,每个VNode的属性与该节点上REAL DOM的属性进行比较。如果存在真实DOM,则循环移动到下一个节点。

2.3如果REAL DOM中有额外的节点,则删除节点

下图显示了REAL DOM VS VNode的差异。

由于存在差异,REAL DOM中的“纽约”节点将被算法删除,如下面的工作流程所示。一切都完成后,该算法还会调用“componentDidUpdate”生命周期事件。

场景3:卸载整个组件

使用案例:假设我们在过滤器中输入了blabla,因为它与“California”或“New York”不匹配,我们根本不会渲染子组件“List”。这意味着,我们需要卸载整个组件。

删除组件类似于删除单个节点。除此之外,当我们删除一个对组件有引用的节点时,框架会调用“componentWillUnmount”,然后递归删除所有DOM元素。在从真实DOM中删除所有元素之后,它调用引用组件的“componentDidUnmount”方法。

下图显示了真实DOM“ul”上对“List”组件的引用。

下图突出显示了流程图中显示删除/卸载组件的工作方式的部分:

—— 完 ——

[翻译]Review——The Inner Workings Of Virtual DOM的更多相关文章

  1. why updating the Real DOM is slow, what is Virtaul DOM, and how updating Virtual DOM increase the performance?

    个人翻译: Updating a DOM is not slow, it is just like updating any JavaScript object; then what exactly ...

  2. React virtual DOM explained in simple English/简单语言解释React的虚拟DOM

    初学React,其中一个很重要的概念是虚拟DOM,看了一篇文章,顺带翻译一下. If you are using React or learning React, you must have hear ...

  3. 深入理解 React 的 Virtual DOM

    React在前端界一直很流行,而且学起来也不是很难,只需要学会JSX.理解State和Props,然后就可以愉快的玩耍了,但想要成为React的专家你还需要对React有一些更深入的理解,希望本文对你 ...

  4. 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?

    引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...

  5. 个人对于Virtual DOM的一些理解

    之前一直认为react的Virtual DOM操作会比传统的操作DOM要快,这其实是错误的,React 从来没有说过 "React 比原生操作 DOM 快".如果没有 Virtua ...

  6. 抛开react,如何理解virtual dom和immutability

    去年以来,React的出现为前端框架设计和编程模式吹来了一阵春风.很多概念,无论是原本已有的.还是由React首先提出的,都因为React的流行而倍受关注,成为大家研究和学习的热点.本篇分享主要就聚焦 ...

  7. 深度剖析:如何实现一个 Virtual DOM 算法

    本文转载自:https://github.com/livoras/blog/issues/13 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步 ...

  8. Virtual DOM 算法

    前端 virtual-dom react.js javascript 目录: 1 前言 2 对前端应用状态管理思考 3 Virtual DOM 算法 4 算法实现 4.1 步骤一:用JS对象模拟DOM ...

  9. React v16-alpha 从virtual dom 到 dom 源码简读

    一.物料准备 1.克隆react源码, github 地址:https://github.com/facebook/react.git 2.安装gulp 3.在react源码根目录下: $npm in ...

随机推荐

  1. [转] Linux 中提高 VsFTP 服务器的安全性

    FTP是互联网应用中的一个元老级人物了,其方便企业用户文件的共享.但是,安全问题也一直伴随在FTP左右.如何防止攻击者通过非法手段窃取FTP服务器中的重要信息;如何防止攻击者利用FTP服务器来传播木马 ...

  2. [译文]casperjs使用说明-选择器

    casperjs的选择器可以在dom下工作,他既支持css也支持xpath. 下面所有的例子都基于这段html代码: <!doctype html> <html> <he ...

  3. C#获取当前程序集的完整路径

    //获取当前程序集的完整路径加上EXE的名称 string binPath = Assembly.GetExecutingAssembly().Location; Console.WriteLine( ...

  4. 怎么使用Vue-cli3开发像iview、element那样的组件可下载直接使用

    https://www.cnblogs.com/wisewrong/archive/2018/12/28/10186611.html 参考这个就行. 而我这篇文章主要是对里面的相关步骤作一些简单的说明 ...

  5. TCP/IP——何时用UDP代替TCP

    UDP和TCP UDP和TCP都有其自身的特点,不同的应用场景和要求需要使用不同的协议来传输,那么何时我们可以用UDP代替TCP呢. UDP 的优点 UDP支持广播和多播,事实上如果应用程序使用广播或 ...

  6. 项目Alpha冲刺 6

    作业描述 课程: 软件工程1916|W(福州大学) 作业要求: 项目Alpha冲刺(团队) 团队名称: 火鸡堂 作业目标: 介绍第6天冲刺的项目进展.问题困难和心得体会 1.团队信息 队名:火鸡堂 队 ...

  7. python 3 map函数用法

    公式 f是定义的函数,l是你的list,所有功能都在f函数里完成, map(f,l) 有些网址爬虫出来的链接是一部分,省略了前端通用的,这时我们需要补充进去, 这时就用到了map函数,批量补充网址, ...

  8. 命令行下class redis not found 解决

    1.在命令行下输入 php --ini 2.在浏览器中查看 phpinfo() 可以看出,我  的phpinfo和命令行的就不是一个php.ini文件.因为我有几个版本的php , 并且在环境变量中配 ...

  9. Git版本回退和撤销修改

    版本回退: 在实际工作中,我们会不断对文件进行修改,然后不断提交修改到版本库里,一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失. ...

  10. SQL Server 保留关键字

    Microsoft SQL Server 2005 使用保留关键字来定义.操作或访问数据库.保留关键字是 SQL Server 使用的 Transact-SQL 语言语法的一部分,用于分析和理解 Tr ...