命令式和声明式各有优缺点,在框架设计方面,则体现在性能与可维护性之间的权衡。 这里我们先抛出一个结论:声明式代码的性能不优于命令式代码的性能。
还是拿上面的例子来说,假设现在我们要将 div 标签的文本内容修改为 hello vue3,那么如何用命令式代码实现呢? 很简单,因为我们明确知道要修改的是什么,所以直接调用相关命令操作即可:
div.textContent = 'hello vue3' // 直接修改
现在思考一下,还有没有其他办法比上面这句代码的性能更好?答案是“没有”。 可以看到,理论上命令式代码可以做到极致的性能优化,因为我们明确知道哪些发生了变更,只做必要的修改就行了。 但是声明式代码不一定能做到这一点,因为它描述的是结果:
<!-- 之前: -->
<div @click="() => alert('ok')">hello world</div>
<!-- 之后: -->
<div @click="() => alert('ok')">hello vue3</div>
对于框架来说,为了实现最优的更新性能,它需要找到前后的差异并只更新变化的地方,但是最终完成这次更新的代码仍然是:
div.textContent = 'hello vue3' // 直接修改
如果我们把直接修改的性能消耗定义为 A,把找出差异的性能消耗定义为 B,那么有:
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = B + A
可以看到,声明式代码会比命令式代码多出找出差异的性能消耗,因此最理想的情况是, 当找出差异的性能消耗为 0 时,声明式代码与命令式代码的性能相同,但是无法做到超越, 毕竟框架本身就是封装了命令式代码才实现了面向用户的声明式。这符合前文中给出的性能结论:声明式代码的性能不优于命令式代码的性能。
既然在性能层面命令式代码是更好的选择,那么为什么 Vue.js 要选择声明式的设计方案呢?原因就在于声明式代码的可维护性更强。 从上面例子的代码中我们也可以感受到,在采用命令式代码开发的时候,我们需要维护实现目标的整个过程, 包括要手动完成 DOM 元素的创建、更新、删除等工作。而声明式代码展示的就是我们要的结果,看上去更加直观, 至于做事儿的过程,并不需要我们关心,Vue.js 都为我们封装好了。
这就体现了我们在框架设计上要做出的关于可维护性与性能之间的权衡。在采用声明式提升可维护性的同时,性能就会有一定的损失, 而框架设计者要做的就是:在保持可维护性的同时让性能损失最小化。