怎么更好的理解虚拟DOM?

虽然Virtual DOM确实是性能杠杠的,可是其实能够说它是无意插柳的一个结果。

React的核心思想:
一个Component拯救世界,忘掉烦恼,今后再也不操心界面。

1. Virtual Dom快,有两个前提
1.1 Javascript很快
Chrome刚出来的时候,在Chrome里跑Javascript很是快,给了其它浏览器很大压力。而如今通过几轮你追我赶,各主流浏览器的Javascript执行速度都很快了。
Julia有一个Benchmark,Julia Benchmarks, 能够看到Javascript跟C语言很接近了,也就几倍的差距,跟Java基本也是一个量级。
因此说,单纯的Javascript其实速度是很快的。
多说一句,这种benchmark并非绝对的依据,由于用这个语言写这个跑得快,并不表明必定是用这个语言写那个也跑得快。

1.2 DOM很慢
关于什么CSS,什么layout那些我不懂,就不瞎说了,咱就说说DOM的结构。
当你用document.createElement()建立一个空的Element的时候(好比建立一个空的div),有如下这几页的东西须要实现(固然,这不是标准,只是个大概的意思):
HTMLElement - Web API Interfaces
Element - Web API Interfaces
GlobalEventHandlers
很是很是多,而且还有很多嵌套引用。
你能够在Chrome console里手动调用document.createElement 而后插入DOM里看看效果。
这仍是一个空的Elemnt,啥内容也没有,就这么复杂。因此说DOM的操做很是慢是能够理解的。不是浏览器不想好好实现DOM,而是DOM设计得太复杂,没办法。

而更糟糕的是,咱们(以及不少框架)在调用DOM的API的时候作得很差,致使整个过程更加的慢。React的Virtual Dom解决的是这一部分问题,它并不能解决DOM自己慢的问题。
好比说,如今你的list是这样,
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
你想把它变成这样
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
一般的操做是什么?
先把0, 1,2,3这些Element删掉,而后加几个新的Element 6,7,8,9,10进去,这里面就有4次Element删除,5次Element添加。
而React会把这两个作一下Diff,而后发现其实不用删除0,1,2,3,而是能够直接改innerHTML,而后只须要添加一个Element(10)就好了,这样就是4次innerHTML操做加1个Element添加,比9次Element操做快多了吧?

固然还有其它一些例子可以优化咱们对DOM的操做,就不举例子了。(其实是由于我举不出例子。。。)

2. 关于React
2.1 接口和设计
在React的设计里,是彻底不须要你操做DOM的。在React里其实根本就没有DOM这个概念的存在,只有Component。当你写好一个Component之后,Component会彻底负责UI,你不须要也不该该去也不可以指挥Component怎么显示,你只能告诉它你想要显示一个香蕉仍是两个梨。
隔离DOM并非由于DOM慢(固然DOM确实慢),而是把界面和业务彻底隔离,操做数据的只关心数据,操做界面的只关心界面。能够想象成把MVC里面的Controller分红两个部分,一部分合并到M里面去,一部分合并到V里面去,就剩下MV,没有C了。。。其实M也并非Model了。推荐看一下Pete Hunt的这个Talk 

重复一遍,React的意思是,我提供一个Component,而后你只管给我数据,界面的事情彻底不用你操心,我保证会把界面变成你想要的样子。
你能够把一个React的Component想象成一个Pure Function,只要你给的数据是[1, 2, 3],我保证显示的是[1, 2, 3]。没有什么删除一个Element,添加一个Element这样的事情。NO。你要我显示什么就给我一个完整的列表。

说到这里,插一句别的,我一开始看到这里还觉得这样的处理方式比较适合通常的WEB应用,写游戏啊什么的可能这个模式不太好用,而后我就看到Pete Hunt那个Talk,说DOOM 3就是这么干的。

。。
。。。
眼泪都下来了,大神们的思路果真我是摸不着边的,洗洗睡吧。

睡醒了接着说。React其实须要从Imperative Programming转换到Declarative Programming去理解。你不要一步一步告诉我这件事情怎么作,什么先和面再剁馅,NO,告诉我你想要煎饼仍是月饼,我会想办法去作的,不要来干扰我。你只须要告诉我有这么一个列表[1, 3, 6]须要显示就好了,不要告诉我怎么显示,我会想办法的,我保证美得冒泡,各类神奇的效果,亮瞎你的钛合金狗眼。

行了行了,你真啰嗦。

。。。

再说几句瞎扯的话,Flux虽说的是单向的Data Flow,可是实际上就是单向的Observer。
Store->View->Action->Store(箭头是数据流向,实现上能够理解为View监听Store,View直接trigger action,而后Store监听Action)
等等,不是说Component是pure function不跟谁绑定吗,为啥View要监听Store?你这个骗子。怪不得都没有人给你点赞。
。。。
。。

咱们仍是继续说React把,Flux是什么鬼,我反正没听过。


2.2 实现
OK,那么,如何实现React呢?
其实对于React来讲,最容易实现的办法是每次彻底摧毁整个DOM,而后从新创建一个全新的DOM。由于一个Component是一个Pure function,根本就没有State这个概念,我又不知道DOM如今是什么样子,那最简单的办法固然是只要你给新数据,我就把整个DOM删了,而后根据你给的数据从新生成一个DOM咯。

等等,Virtual DOM哪儿去了?

事实是这样的,最简单实现React的方式虽说很是简单,可是效率实在是过低了,你竟然要所有都删了重建DOM,DOM自己已经很慢了,你还这么去用,谁能忍啊?

而后Virtual DOM就来救场了。

Virtual DOM和DOM是啥关系呢?
首先,Virtual DOM并无彻底实现DOM,Virtual DOM最主要的仍是保留了Element之间的层次关系和一些基本属性。由于DOM实在是太复杂,一个空的Element都复杂得能让你崩溃,而且几乎全部内容我根本不关心好吗。因此Virtual DOM里每个Element实际上只有几个属性,而且没有那么多乱七八糟的引用。因此哪怕是直接把Virtual DOM删了,根据新传进来的数据从新建立一个新的Virtual DOM出来都很是很是很是快。(每个component的render函数就是在作这个事情,给新的virtual dom提供input)

因此,引入了Virtual DOM以后,React是这么干的:
你给我一个数据,我根据这个数据生成一个全新的Virtual DOM,而后跟我上一次生成的Virtual DOM去 diff,获得一个Patch,而后把这个Patch打到浏览器的DOM上去。完事。

有点像版本控制打patch的思路。
假设在任意时候有,VirtualDom1 == DOM1 (组织结构相同)
当有新数据来的时候,我生成VirtualDom2,而后去和VirtualDom1作diff,获得一个Patch。
而后将这个Patch去应用到DOM1上,获得DOM2。
若是一切正常,那么有VirtualDom2 == DOM2。

这里你能够作一些小实验,去破坏VirtualDom1 == DOM1这个假设(手动在DOM里删除一些Element,这时候VirtualDom里的Element没有被删除,因此两边不同了)。
而后给新的数据,你会发现生成的界面就不是你想要的那个界面了。


最后,回到为何Virtual Dom快这个问题上。
实际上是因为每次生成virtual dom很快,diff生成patch也比较快,而在对DOM进行patch的时候,我可以根据Patch的内容,优化一部分DOM操做,好比以前1.2里的那个例子。
重点就在最后,哪怕是我生成了virtual dom,哪怕是我跑了diff,可是我根据patch简化了那些DOM操做省下来的时间依然很可观。因此整体上来讲,仍是比较快。

简单发散一下思路,若是哪一天,DOM自己的已经操做很是很是很是快了,而且咱们手动对于DOM的操做都是精心设计优化事后的,那么加上了VirtualDom还会快吗?
固然不行了,毕竟你多作了这么多额外的工做。

可是那一天会来到吗?
诶,大不了到时候不用Virtual DOM。浏览器

相关文章
相关标签/搜索