最近在看这本书,很好的一本书。从高层的设计角度探讨框架需要关注的问题,从而我们能够更好的理解Vue.js中一些具体的实现为何要做出这样的选择。
第一篇 框架设计概览
1 权衡的艺术
框架设计里到处都体现了权衡的艺术。这是因为当设计一个框架的时候,框架本身的各个模块本身并不是相互独立的,而是相互关联、相互制约的。而我们学习框架的时候,也应该从全局的角度对框架的设计拥有清晰的认知,否则容易被细节所扰看不清全貌。
1.1 命令式和声明式
视图上看,框架分为命令式和声明式。命令式框架的一大特点就是关注过程;声明式框架更加关注结果。
声明式框架关注结果,而对于如何实现的这个结果的过程,我们并不关心,由Vue.js帮我们完成。换句话说,Vue.js帮我们封装了过程。因此,我们能够猜到Vue.js的内部实现一定是命令式的,而暴露给用户的却更加声明式。
1.2 性能与可维护性的权衡
先给出结论:声明式代码的性能不优于命令式代码的性能。
因为对于框架来说,为了实现最优的更新功能,需要找到前后的差异并只更新变化的地方。
如果我们把直接修改的性能消耗定义为A,把找出差异的性能消耗定位为B,那么有:
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = B+A
可知: 声明式代码的性能不优于命令式代码的性能。(毕竟框架本身就是封装了命令式代码才实现了面向用户的声明式)
那Vue.js为什么选择声明式代码呢?原因就在于声明式代码的可维护更强。
这就体现了框架设计上要做出关于可维护性与性能之间的权衡。即:在保持可维护性的同时让性能损失最小化。
1.3 虚拟DOM的性能到底如何
我们已知:声明式代码的更新性能消耗 = 找出差异的性能消耗+直接修改的性能消耗
因此,我们如果能找出最小化差异性能消耗,就可以让声明式代码的性能无限接近命令式代码。
而所谓的虚拟DOM,就是为了最小化找出差异这一步的性能消耗而出现的。
虚拟DOM解决的问题具体说就是,能够让我们不用付出出太多的努力(写声明式代码),还能保证应用程序的性能下限;甚至想办法逼近命令式代码的性能。
比较innerHTML和虚拟DOM的性能:
innerHTML创建页面的性能:HTML字符串拼接的计算量+innerHTML的DOM计算量
DOM创建页面的性能:创建JavaScript对象的计算量+创建真实DOM的计算量
这两者创建页面的性能差距并不大(只看宏观上数量级的差异)
innerHTML更新页面的过程是:重新构建HTML字符串,在重新设置DOM元素的属性。而重新设置innerHTML属性就等价于销毁所有旧的DOM元素,再全量创建新的DOM元素。
虚拟DOM更新页面的过程:重新创建JavaScript对象,然后比较新旧虚拟DOM,找到变化的元素并更新它。
这样更新页面时的优势就显而易见,我们分三个维度:心智负担,可维护性和性能。虚拟DOM声明式,因此心智负担小,可维护性强,性能虽比不上极致优化的原生JavaScript,但是在保证心智负担和可维护性的前提下相当不错。
1.4 运行时和编译时
当设计一个框架时,我们有三种选择:纯运行时、运行时+编译时、纯编译时。
纯运行时的框架,由于没有编译的过程,因此我们没办法分析用户提供的内容;加入编译后,我们就可以分析用户提供的内容了,可知道哪些内容未来可改变,哪些永远不会改变,这样就能在编译的时候就提取这些信息,然后之后可做进一步的优化了。
其实,一个框架既可以是纯运行时的,也可以是纯编译时的,还可以是既支持运行时又支持编译时的。而Vue.js3仍然保持了运行时+编译时的架构,在保持灵活性的基础上能够尽可能地去优化。
1.5 总结
这章学到了:命令式更加关注过程,而声明式更加关注结果。而框架设计者牺牲了一些性能从而追求声明式代码的可维护性高。所以之后的过程中要想办法使性能损耗最小化。
之后就是虚拟DOM的性能,声明式更新时的性能损耗 = 找出差异的性能消耗 + 直接修改的性能消耗;虚拟DOM的意义在于找出差异的性能消耗最小化。结合心智负担、可维护性、性能等,于是发现,虚拟DOM更新页面时的策略更为优秀。
最后,了解了运行时和编译时,并且总结出Vue.js3是一个编译时 + 运行时的框架,在保持灵活性的基础上,还能够通过编译手段分析用户提供的内容,从而进一步提升更新性能。