JavaScript进阶(三十):理解虚拟 DOM

这里我们就来说一说虚拟 DOM 这个东西,首先我们必须得先把它的概念弄明白了,那么先说下我是怎么理解虚拟 DOM 的。

虚拟 DOM 它跟我们别的东西不太一样,比方说,我们在 js 里面写一个类,不用想,就是 class,只要你用的是 ES6,全天下就这一个写法。

再比方说,我想给某个东西加样式,那我如果用 id 来获取元素,最多就是 getElementById,或者 querySelector,就这么几种方法,因为这些都是一个标准化的东西。

 

虚拟 DOM,其实并不是一个标准,什么意思呢?

我们平常用的那些标准,都是 W3C 搞出来的,而虚拟 DOM 到目前为止,更多的,其实还是大家自己搞出来的一个东西。并且虚拟 DOM 这个东西,说句特别实在的话,它在很长的一段时间之间,估计都不会成为 W3C 的一个标准。

 

那为什么我有这个把握呢?原因很简单,因为假设它成为标准了,其实是没有意义的,为什么?

因为我们为什么要用虚拟 DOM,而不用普通的 DOM?就是因为普通的 DOM 性能太差了。

那如果从官方的角度考虑,与其说在另搞一套东西出来,还不如把原来那个优化好了,所以说在我看来,虚拟 DOM 估计很长的一段时间,应该不会成为标准的一部分。

 

所以说白了,虚拟 DOM 没有标准可言,我写虚拟 DOM,可能喜欢这样写,他写可能是那样的,谁都能写出个不一样的感觉来。

当然,好在现在,能够自己独立编写一整套虚拟 DOM 库的人,相对来说还比较少,所以并没有导致一个特别大的混乱。

所以这东西没什么写的标不标准,我想怎么写,就怎么写,只要性能上靠谱,用起来方便,它就是对的。

 

在我们前面的博客里面,一直有提到虚拟 DOM,那么这里我们就详细的来讲讲,首先,当初这个虚拟 DOM 为什么而产生?

我们知道真实的 DOM,其实它的特点就是慢,特别的慢。它的操作性能,比普通操作要慢好几千倍,在我这台电脑上,一般测出来的是 3000 - 4000 倍左右。

所以真实的 DOM 非常慢,那么这个时候,我们一定要做到一点,就是说之所以虚拟 DOM 能够产生作用,原因就在于很简单的一点,你要尽量的,减少去使用真实的 DOM。

说白了,就是你就只进行最少限度的真实 DOM 操作,尽可能的让虚拟 DOM 来承担你的东西。

 

然后,虚拟 DOM 这个东西,其实你不要把它想的太复杂了,好厉害的感觉,完全不是,它就跟我们平常用的对象差不多。

说白了,它就是一个普通的对象,只不过,它上面是精确的去复制了,我们真实 DOM 它应该有的东西。

最后,我们还关心一个事,就是虚拟 DOM 它所产生的任何变化,我们需要想办法去给它同步回真实的 DOM,就是更新 UI,页面也得跟着变。

 

然后接下来,我们就直接来试试,看看虚拟 DOM 具体怎么做。

首先,我们先不管别的,别一上来,就追求完美,规划的特别好等等,不存在的,这个世界上就没人能做到,一开始的时候面面俱到,想的特清楚,后来完全不用改,这是扯淡。

所以现在这时候,我们先别管,也不用想,就先用最笨的方法,能出来东西就行,然后后面在逐渐去改进它,实在太乱写不下去了,我们还可以去重构。

 

那么现在我们就来一个例子,比方说,我这里面有个 div,它上面有一些属性:

然后它里面还有一些东西:

那么现在这个时候,真实的 DOM 节点有几个?

div,h2,ul,3 个 li 一共 6 个,并且这 6 个真实的节点之间,它们还是有一定的层级关系。

这些都是我们需要去仿照出来的,虚拟 DOM 的结构,一定是跟真实 DOM 保持一致的。

而且,这里面有可能涉及到各种不同的节点,比如我在中间加了一堆文字:

当然,99% 的情况下,我们一般只关心 2 种节点。

一种是元素,而另一种就是文本节点,因为这些文本里面,它里面可能包含一些模板之类的东西,比如 {{ }}。

所以,我们主要关心元素和文本这两种节点。

 

那么既然它这有两种节点,说的实在一点,我希望我们虚拟 DOM 那边的虚拟节点,也是两种,为什么?因为这样我处理起来简单一些。

在我那个用来模拟元素的节点当中,我只需要管好跟元素相关的事,别的我不管。

然后跟文本相关的那个节点当中,我就直接管好文本相关的事就行,别的也跟我没关系。

 

所以,既然我们现在有了这样的一个想法之后,就可以做一个事。

首先,这两种节点,你别看它有一些区别和不同,但是,它们其实还是有一样的地方。

比方说,元素和文本是不是都有父级?我能不能去更新一个文本节点的内容?我能不能去更新一个元素上面的东西,比如 title 变了。

所以说,在这种情况下,你会发现,它们之间还有一些共通的东西。

那么这个时候,我们就可以画一个草图,虚拟元素和虚拟文本这两个东西,它们之间,绝对不是相互之间毫无关联的,而是有很多共通的东西。

所以就我个人而言,我更希望它是一个继承的关系:

VNode 是父级,假设我将来想加个别的什么节点,我也可以从它里面来继承。

VElement,虚拟元素节点。

这里顺便一说,有人可能会想,你这个 VElement 不太合理,因为 Element 它毕竟是一个很通用很广义的东西,它指的是所有的元素,那我们有没有可能,在搞出一个 VDiv,VSpan 之类的东西呢?

其实有的框架是这么做的,比如 react,它里面就是所有的 HTML 元素,它都有对应的抽象。这样做的好处,就是它每一个独立出来的那个类,都可以更好的处理这个节点。

因为说白了,你比方说像 a,它是不是有一些特殊的东西,比方说 a 有 href,你能找出第二个有 href 的东西来吗?所以这个 href 并不是所有人都有,那这时候,如果我用一个 AElement 来处理,是不是更加的方便?

当然,我们这里具体就知道有这么个事就行,现在我们也不会去抽象那么多级,不然太麻烦了,就用一个 VElement,来代表所有。

VText,虚拟文本节点。

VElement 和 VText 这两种节点,都是以 VNode 作为父级来继承出来的,这样的话,很多相关的东西,我们都可以放到 VNode 里面,所以按照这种说法, 我们就可以搞一个公共的父级,然后它们两个分别来继承,这是最合理的。

 

那么首先,我们这有几个东西。

首先是 VNode.js,广义上的,泛指所有节点。

元素,注释,文本这些都是节点。

然后接下来,我们肯定还需要两个东西。

一个是 VElement.js,它的作用,就是专门的用来描述一个元素。

当然,它不是凭空从石头缝里面蹦出来的,它是从父级继承来的:

以及,我们还需要一个节点 VText,它是一个虚拟的文本节点,我们以后对文本的各种更新,都是从它上面走:

最后,我们还需要引入它们:

那么现在我们就已经抽出来这三种东西。

下篇博客,我们就要来考虑考虑,它们都分别有什么。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章