这本书主要是从顶层的角度来观察Vue3框架的设计,首先阐述了设计一个框架需要考虑的问题,然后阐述了框架内部各个模块的设计方式。

因为在Vue3模块中,存在各个模块,每个模块都有属于自己的功能的细节,这些模块之间是相互联系的。


第一章、权衡的艺术

设计一个框架是给人使用的,开发框架目的就是更方便,简单的进行写代码。也就是说要让使用者的心智负担要比较小。所以说框架的设计者就要权衡各个方案的优缺点,做到一个平衡。
所以作者讨论了在设计Vue3的时候做的三点权衡。是使用命令式还是声明式,各种方案的性能,是运行时还是编译时。

命令式还是声明式

命令式:即使程序按照我们的写的指令,一条一条的执行,就是命令式。
举个例子:我们要获取页面上的一个盒子,并给这个盒子添加一个文本,然后给盒子添加一个点击事件。

  1. const div = document.querySelector('#app')
  2. div.innerText = 'hello world'
  3. div.addEventListener('click',()=>{alert('点击了div盒子')})

上面代码的步骤是

  1. 先获取盒子
  2. 然后给盒子添加文本
  3. 最后给盒子添加点击事件

可以看到是我们要做什么就按照顺序将要做的用代码一条条写出来,这就是说为的命令式。

声明式: 声明式更加的关注结果。vue就是一个声明式的框架。
下面我们举个例子,在vue中我们要做到和上面一样的效果,只需要一行代码即可。

  1. <div @click = ()=>{alert('点击了div盒子')}>hello world</div>

可以看到在vue中,我们就是提供了一个结果,而这个结果是如何得到的,我们并不关心,这就是声明式代码。

其实声明式的内部就是命令式的代码,换句话说,vue帮我们封装了过程(命令式代码),而暴露给开发者的是结果(就是让开发者可以写声明式代码)

命令式代码和声明式代码的优缺点:前者的性能好于后者,但是后者的代码可维护性强于前者。

因为框架本身就是封装的命令式的代码,才实现了面向用户的声明式,所以说声明式代码的性能是不可以由于命令式的代码的性能
也就是说声明式的框架,是牺牲了点性能来换取代码的可维护性的。这就是做的第一个权衡,为了提高可维护性而选用了声明式。

关于修改DOM的性能消耗

从上一小节,我们知道vue是声明式的框架,所以说它的性能消耗是比较高的,为了减少性能的消耗,在vue中采用了虚拟DOM的方式。
所以声明式代码的性能 = 找出差异的性能消耗 + 直接修改的性能消耗。
所以说如果我们能最小化的找出差异的性能消耗 (虚拟DOM+diff找出差异), 就是使得声明式代码的性能消耗达到最小。
在js中主要有三种操作DOM的方式。

  1. innerHTML (有别于document.createElenment 来操作DOM)
  2. 虚拟DOM
  3. 原始JS(直接document.createElenment)

作者这里主要对比了使用innerHTML操作页面和虚拟DOM操作页面的性能。
创建页面时的性能

虚拟DOM innerHTML
js层面的消耗 创建JS对象 创建 对于的字符串
DOM层面的消耗 新建所有的DOM 新建所有的DOM

可以看到两者的区别不大。

在页面更新的时候的性能

虚拟DOM innerHTML
js层面的消耗 Diff+需要更新的JS对象 创建一个新的字符串
DOM层面的消耗 必要的DOM更新 销毁全部的DOM
生成全部的新DOM

可以看到在更新页面的时候,影响虚拟DOM的性能因素与影响innerHTML的性能因素不同。
对于虚拟DOM来说 ,无论页面的大小,性能的消耗之和要更新变化的内容有关
对于innerHTML来说,一点点变化都需要生成和销毁全部的DOM,所以性能的消耗和页面的大小有关,页面越大性能消耗越大。
三种页面更新时候性能消耗的总结:

innerHTML 虚拟DOM 原生JS
心智负担中 心智负担小 心智负担大
性能差 性能中等 性能高
可维护性强 可维护性差

综上所述,在维持性能不错的情况下,虚拟DOM可以做到更小的心智负担和更强的代码维护性。

运行时和编译时

首先我们介绍下什么时运行时,什么时编译时。
运行时,就是说用户直接传入虚拟DOM,然后我们通过render(虚拟DOM) 函数进行原始的创建和页面的渲染工作。
编译时,就是说我们直接将树形的HTML 编译成对于的DOM树。(例如:innerHTML的字符串类型的树形结构,我们直接通过compiler进行编译,直接转化为对应的DOM)。

在上文说到vue中采用了虚拟DOM,那vue是运行时的吗?其实不是的,vue是运行时+编译时的。在vue中允许你直接通过h函数传入对应的js对象,然后通过render来构建DOM树。但是其实在大部分时候,我们都不是这样使用的。我们都是直接通过在<template></template> 写对应的HTML结构,其实在vue中,vue使用vue-compiler进行了编译,编译成了虚拟DOM(不是真实DOM),然后再将虚拟DOM通过render进行了声明真实的DOM。
所以说vue是运行时+编译时的。