权衡的艺术
引入
设计框架要从全局考虑权衡
比如框架应该设计成命令式还是声明式,纯运行还是纯编译还是运行时+编译时
命令式和声明式
命令式
特点:关注过程
用自然语言描述能够与代码产生一一对应的关系
例子:自然语言描述转化为代码
//获取id为app的div标签
//他的文本内容是hello world
//为其绑定点击事件
//点击时弹出提示OK
代码:
const div = document.querySelector('#app')
div.innerText = 'hello world'
div.addEventlistener('click', () => {alert('OK')})
声明式
特点:关注结果
用vue实现上述代码:
<div @click="() => {alert('OK')}">hello world</div>
可以知道,vue.js帮我们封装了过程,内部是命令式,暴露给用户的更加声明式
性能和可维护性
结论:声明式代码的性能并不优于命令式代码的性能
例子:对上面的代码中的文本内容改成hello vue3
-
命令式的操作:
div.textContent = 'hello vue3' -
声明式的操作:
<div @click="() => {alert('OK')}">hello vue3</div>
对比上述代码可以知道,对于声明式来说,框架需要先找到差异再只更新变化的地方
所以声明式要比命令式消耗多一部分查找差异时候的性能
但是vue在权衡两者的性能时,还是选择了声明式,因为其代码好维护,省去了手动创建元素等操作
虚拟DOM的性能如何
这里主要比较innerHTML与虚拟DOM性能
创建页面时性能
innerHTML创建页面的性能:HTML字符串拼接的计算量 + innerHTML的DOM计算量- 虚拟DOM创建页面的性能:创建JS对象的计算量 + 创建真实DOM的计算量
在创建页面的性能比较上,两者性能上并没有很大的区别,虚拟DOM也没有优势
更新页面时性能
-
innerHTML更新页面的过程:重新构建HTML字符串,在重新设置DOM元素的
innerHTML属性也就是说,哪怕只改了一个字,都要重新设置
innerHTML属性重新设置
innerHTML也就意味着销毁所有的旧的DOM元素,再全量创建新的DOM元素性能公式:渲染HTML字符串 + 销毁所有旧DOM + 新建所有新DOM
性能因素:与模板的大小有关
-
虚拟DOM更新页面:
重新创建新的JS对象(虚拟DOM树),再比较新旧虚拟DOM树,找到变化并更新
性能公式:创建新的JS对象 + Diff + 必要的DOM更新
性能因素:与数据变化量有关
在更新页面的性能比较上,虚拟DOM总体来说要比innerHTML更加优秀
与原生比较
原生JS:由于是手动创建删除修改DOM元素,所以他的性能是最高的,但是可维护性差
虚拟DOM:性能不错,可维护性强
innerHTML:性能差,可维护性中等
运行时和编译时
运行时
用户直接输入指定的数据结构数据,去直接运行
比如生成一棵树,用户需要输入指定的树形结构,再使用一个递归函数去生成树
运行时+编译时
按照上面的例子,用户可以传入HTML字符串去生成树
过程也就是先将HTML转化成树形数据结构,再将该数据结构转化成树
这种方式既支持运行时,即用户直接传入数据对象生成树,又支持编译时,用户直接传入HTML字符串,将其编译为数据对象再交给运行时处理
编译时
直接将HTML转化为命令式代码,但是需要放入编译器编译运行
三者比较
- 运行时框架:没有办法分析用户提供的内容
- 编译时+运行时:可以分析用户提供的内容,提取有用信息
- 编译时框架:直接编译成可执行的JS代码,性能可能会更好,但是有损灵活性,即用户的内容必须编译后才能用
Vue3采用了运行时+编译时架构,在保持灵活性的基础上尽可能去优化