权衡的艺术

90 阅读4分钟

一、什么是虚拟DOM

Virtual DOM(虚拟DOM,简称VDOM)是实现“根据自变量变化计算出UI变化”的一种主流技术

工作原理:

  1. 将“元素描述的UI”转化为“VDOM描述的UI”。
  2. 对比变化前后“VDOM 描述的UI”,计算出UI中发生变化的部分。

二、命令式与声明式

众所周知,试图层框架通常分为命令式和声明式,它们各有优缺点。早年流行的jQuery就是典型的命令式框架,而如今流行的Vue和React则是声明式框架。那么两者究竟有什么区别呢?

命令式框架的一大特点就是关注过程。例如,我们把下面的这段话翻译成代码:

-获取id为app的标签
-它的文本内容为hello,world
-为其绑定点击事件
-当点击式提出提示:ok

对应代码:

$('#app) // 获取div
    .text('hello world')   /  绑定点击事件/设置文本内容
    .on('click',()=>{alert('ok')}) //

声明式框架与命令式框架关注过程不同,声明式框架更加关注结果。结合Vue.js,我们来看看如何让实现上面的功能:

<div @click="()=>alert('ok')">hello word</div>

这段类似HTML的模板就是Vue.js实现如上功能的方式。通过代码可以知道声明式框架之关注结果,而过程我们并不关心。而这个“结果”的过程则是由Vue.js帮我们完成的,因此我们也可以猜到Vue.js内部实现一定是命令式的。

三、性能与可维护性

命令式和声明式框架各有优缺点,在框架的设置方面则体现在性能与可维护性之间。首先我们可以先给出一个结论:声明式代码的性能不会优于命令式代码的性能。

为什么声明式代码的性能不会优于命令式代码的性能?原因很简单,因为我们知道要修改什么,所以直接调用相关命令操作即可:

div.textContent = 'hello world2'  //  直接修改

而声明式的描述结果是:

<-- 之前 -->
<div @click="()=>alert('ok')">hello word</div>
<-- 之后 -->
<div @click="()=>alert('ok')">hello word2</div>

对于声明式框架来说为了实现最优的更新性能,他需要找到前后的差异然后只更新变化的地方。 因此我们可以知道:

-命令式代码的更新性能消耗 = 直接修改的性能  
-声明式代码的更新性能消耗 = 直接修改的性能 + 查询差异的性能消耗

理论上命令式代码可以做到极致的性能优化。当然这也只是理论上要不然现在流行的框架就不是声明式框架而是命令式框架了。

四、虚拟DOM

上面我们讲到,声明式代码的更新性能消耗 = 直接修改的性能消耗 + 找出差异的性能消耗。而虚拟DOM就是为了最小化的找出差异这一步的性能消耗而出现的。因此理论上虚拟DOM的更新技术的性能是不可能比原生操作DOM更高。但是在大部分的情况下我们很难写出绝对优化的命令式代码,尤其是当程序的规模很大的时候,即使写出来了也是花费了很多时间和精力,这样投入产出比太低了,不适合如今日新月异的互联网环境。

那么为什么如今前端流行的框架都是采用虚拟DOM。其实如果是首次渲染,虚拟DOM不具有任何优势,甚至它要进行更多的计算,消耗更多的内存。 虚拟DOM的优势在于Diff算法和批处理策略,在页面更新之前,提前计算好了如何进行更新和渲染DOM。 所以,虚拟DOM帮助我们提高了开发效率,而不是一定要去比普通DOM快。

虚拟DOM的优势如下: 简单方便,如果使用手动操作真实DOM完成页面,繁琐并容易出错,在大规模应用下维护起来也很困难。 性能方面,使用虚拟Dom能够有效避免真实DOM树的频繁更新,减少重绘和回流提高性能。 跨平台,虚拟DOM具有跨平台的能力,一套代码多端运行。 缺点:是因为首次渲染大量DOM,由于多一层虚拟DOM计算,速度比正常稍慢。

对于一个优秀的框架来说,我们需要考虑:用户的心智负担,代码的可维护性以及性能。其中直接操作原生DOM元素的性能是最高的,但是为了使代码的性能最高程序员往往需要承担巨大的心智负担,且写出来的代码也极难维护,特别是项目换人之后。综合以上原因声明式框架无疑是最适合当下的前端框架。