Vue.js设计与实现(1-1)

33 阅读4分钟

框架设计概览#权衡的艺术

  • 作为框架设计者,一定要对框架的定位和方向拥有全局的把控,这样才能做好后续的模块设计和拆分。
  • 作为学习者,在学习框架的时候,也应该从全局的角度对框架的设计拥有清晰的认知,否则很容易被细节困住,看不清全貌。

一、权衡的艺术:命令式和声明式

  1. 命令式框架:jQuery

关注过程:自然语言描述能够与代码产生一一对应的关系,代码本身描述的是“做事的过程”,这符合我们的逻辑直觉。

$('#app)               // 获取div
  .text('hello world') // 设置文本
  .on('click', () => { // 绑定点击事件
    alert('ok')        // 弹出提示
  })
  1. 声明式框架:Vue.js

关注结果:我们提供以一个“结果”,至于如何实现这个“结果”,我们并不关心。Vue.js帮我们封装了过程

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

Vue.js的内部实现是命令式,而暴露给用户的却更加声明式。

范式特点优点
命令式关注过程性能好
声明式关注结果可维护性强

二、权衡的艺术:性能与可维护性的权衡

声明式代码的性能不优于命令式代码的性能。

框架本身就是封装了命令式代码才实现了面向用户的声明式。

理论上,命令式代码可以做到极致的性能优化,因为明确知道哪些发生了变更,只做必要的修改就行了。

对于框架来说,为了实现最优的更新性能,需要找到前后的差异并只更新变化的地方,然后再执行更新操作。

如果把直接修改的性能消耗定义为A,把找出差异的性能消耗定义为B,那么有:

  • 命令式代码的更新性能消耗 = A
  • 声明式代码的更新性能消耗 = B + A 声明式代码会比命令式代码多出找出差异的性能消耗

在保持可维护性的同时,让性能损失最小化。

三、权衡的艺术:虚拟DOM的性能到底如何

虚拟DOM,为了最小化找出差异这一步的性能消耗而出现的:写声明式代码,保证应用程序的性能下限。

1. 新建页面

  • innerHTML为了渲染出页面,首先要把字符串解析成DOM树 —— 涉及DOM的运算要比JavaScript层面的计算性能差

通过innerHTML创建页面的性能 = HTML字符串拼接的计算量 + innerHTML的DOM计算量。

  • 虚拟DOM创建页面的过程分为2步:第一步是创建JavaScript对象 —— 真实DOM的描述;第二步是递归遍历虚拟DOM树,并创建真实DOM。

通过虚拟DOM创建页面的性能 = 创建JavaScript对象的计算量 + 创建真实DOM的计算量。

性能影响因素虚拟DOMinnerHTML
纯JavaScript运算创建JavaScript对象(VNode)渲染HTML字符串
DOM运算新建所有DOM元素新建所有DOM元素

宏观上,在同一个数量级,在创建页面的时候,都需要新建所有DOM元素 —— 两者性能没有差异。

2. 更新页面

  • 使用innerHTML更新页面的过程是:重新构建HTML字符串,再重新设置DOM元素的innerHTML属性。而重新设置innerHTML属性就等价于销毁所有旧的DOM元素,再全量创建新的DOM元素。
  • 使用虚拟DOM更新页面的过程是:重新创建JavaScript对象(虚拟DOM树 —— VNode),然后比较新旧虚拟DOM,找到变化的元素并更新它。
性能影响因素虚拟DOMinnerHTML
纯JavaScript运算创建新的JavaScript对象(VNode)+ Diff渲染HTML字符串
DOM运算· 必要的DOM更新· 销毁所有旧DOM
· 新建所有新DOM
性能因素与数据变化量有关与模板大小有关

在更新页面时,虚拟DOM在JavaScript层面的运算要比创建页面时多出一个Diff的性能消耗,但毕竟是JavaScript层面的运算,不会产生数量级的差异。而在DOM层面的运算,只会更新必要的元素。但innerHTML需要全量更新。

宏观上,在同一个数量级,在更新页面的时候,虚拟DOM的性能优于innerHTML。

另外,当更新页面时,影响虚拟DOM的性能因素与影响innerHTML的性能因素不同。对于虚拟DOM来说,无论页面多大,都只会更新变化的内容;而对innerHTML来说,页面越大,更新时的性能消耗越大。

3. 小结

innerHTML、虚拟DOM以及原生JavaScript(createElement等方法)在更新页面时的性能对比,如图:

image.png

四、权衡的艺术:运行时和编译时

  1. 纯运行时,无法分析用户提供的内容。
  2. 运行时 + 编译时:Vue.js 3,保持灵活性的基础上尽可能去优化性能。
  3. 纯编译时:Svelte,直接编译成可执行的JavaScript代码,性能可能会更好,但有损灵活性 —— 用户提供的内容必须编译后才能用。

五、权衡的艺术:总结

性能分析是一个多因素权衡过程。