vlambda博客
学习文章列表

设计一个虚拟Dom,其实没那么难!

点击 上方蓝字 关注我们


阅读本文大概需要5分钟。


老陈发现从17年开始一些响应式的框架就越来越火,之前在读书的时候就觉得这玩意肯定能火,16年就有简单的使用。然后,最近一次小面试问到了如何设计一个虚拟dom,就简单说了是一个dom映射在js对象里面的东西,感觉还是不够深入。所以有了现在这篇文章啦。

一、前端应用的数据状态管理

假设,我们有一个应用,应用里面有一个数据表,数据表会根据某个字段进行升序或者降序排序展示,这个跟数据库表很像哈,那么我们怎么在页面显示出来呢?

我们顺着自己的逻辑来:

我们设定一个字段名,设定一个升序或者降序的标识,然后通过点击之后,对对象进行排序,再利用js插入页面更新视图。

其实这么做你会发现一个问题,我们要维护的字段特别多,那么点击事件和更新视图的操作就会越来越多。所以我们发现出现了MVC、MVP的架构模式,这只能解决一部分问题,代码组织维护的问题,不能减少操作DOM的次数,该操作还是得操作。

所以那么多的问题,怎么解决呢?我们需要做一个东西,可以让视图和状态绑定,状态变更了视图就自动变更。我们就不需要手动去更新页面。 就是MVVM就呼之欲出了。

只要在模板中声明视图组件是和什么状态进行绑定的,双向绑定引擎就会在状态更新的时候自动更新视图。

他其实解决的是状态维护的问题,大大减少了代码中的视图更新逻辑,但是这不是唯一的办法,还有一个简单粗暴的办法可以大大降低视图更新的操作,一旦状态发生变化,就用模板引擎重新渲染整个视图,替换掉旧的视图,只要用户触发点击事件就更新状态,这样也不需要手动操作DOM了。

innerHTML就完事,这样做就是有一个很不好的弊端,频繁操作DOM,会使性能大大降低,这样的性能成本实在是太高了。

这时候,虚拟DOM可以出现了。

二、Virtual DOM算法

DOM特别慢,记住他,一个div的元素上面就附带着各种各样的属性值,如果你操作可能就会触发页面重排,这就是性能一直被吐槽的原因。

如果是JS的对象,处理起来会更简单,我们可以将DOM树上的结构,属性都用JS表示出来。

let element = { tagNmae: 'div', //节点标枪 props: { id: 'items' }, children: [  {tagName'div'props: {class'item'}, children: ["Item 1"]},  {tagName: 'div', props: {class: 'item'}, children: ["Item 2"]},  {tagName: 'div', props: {class: 'item'}, children: ["Item 3"]}, ]}
<div id="items">  <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div></div>

可以仔细看一下上面两个对应关系,我们可以用JS对象表示的树来构建一颗真正的DOM树。

到这,你有没有发现我们都在做无用功,一个JS对象来构建DOM树,重新渲染这个JS对象,该更新还是得更新,搞毛呀。

所以,我们需要对这个树进行一些特殊的操作,就是将新树和旧树进行对比,这样我们到真正对页面进行DOM操作就只会变更需要变更的东西。这一步就是所谓的虚拟DOM算法。

我们可以给虚拟DOM下结论,他其实就是给JS操作和DOM之间加一层,用于对比,虚拟DOM是DOM的一个映射。

三、diff算法

比较两个树的差异是虚拟DOM算法最核心的部分,所谓虚拟DOM的两个树之间的diff是一个时间复杂度为O(n^3)的问题,但是在前端,我们只会对同一层进行对比也就是O(n)的时间复杂度。


主要要设计一个虚拟的DOM的话要有三个步骤,第一个是一个DOM到虚拟DOM的映射对象,第二个是diff算法的设计,第三对存在差异的部分进行DOM操作。

看到这里,你应该对虚拟DOM有所了解了,有点标题党了,如果大家有兴趣我在把写好的虚拟DOM部分的源码分享出来。


   扫描关注

老陈说前端

想象不到的干货