Vue.js设计与实现
# 第一章 权衡的艺术
# 1.1 命令式和声明式
命令式就是JQuery,也即是注重过程,把一个操作分解成一个一个命令,告诉浏览器需要做些什么。 例如下面这段话
- 获取id为app的div标签
- 他的文本内容为hello world
- 为其绑定点击时间
- 当点击时弹出提示:OK
2
3
4
用jQuery实现的话,就是
$('#app') //获取div
.text('hello world') //设置文本内容
.on('click',()=>{alter('OK')}) //绑定点击事件
2
3
声明式则是关注结果,只需要给框架声明一个特定的模板,它就会自动处理
<div @click="()=>{alter("OK")}">hello world</div>
# 1.2 性能与可维护性的权衡
结论,声明式代码的性能不优于命令式代码的性能
因为命令式想要更新DOM的内容,只需要调用div.textContent这种方式就可以,也就是执行一次
但是如果是声明式,它会先找出两次声明时候的不同,然后再去更新内容。也就是执行了两步操作
# 1.3 虚拟DOM的性能到底怎么样
前面说了,声明式在更新内容的时候,会多一步找出差异的步骤。如果这个步骤的性能消耗等于0,那它的性能就会逼近命令式的。
但是呢,就拿jQuery的innerHTML来说,如果const html = <div><span>text</span></div>
,然后我执行div.innerHTML = html
。这个时候,执行的代码没有想象的简单,他会先解析HTML为DOM树,然后创建DOM。
我们知道DOM操作是非常慢的,远不及js层面的计算。
如果我想要更改html中间的text为text2,那么innerHTML就会摧毁原本的两个dom,然后新建两个。
但同样的效果放在虚拟DOM下呢?
虚拟dom会先创dom对应的js对象,然后渲染成为DOM。在更改text内容的时候,也会创建新的DOM的对象,然后对比两次对象的差异,最后把有差异的DOM对象找出来,更新真实的DOM。
所以虚拟DOM在有不错的性能下,也少带来心智负担。是一个权衡利弊的做法。
# 1.4 运行时和编译时
纯运行时:
我们设计了一个框架,有一个render函数,只要你提供一个js对象,他就会渲染成为DOM。
运行时+编译时
然后用户就开始用了,用了一段时间之后发现,写js对象实在是太麻烦了,能不能像写HTML那样声明结构呢?
于是你多设计了一个compiler函数。
跟用户说,你只要像HTML那样,写标签,我就帮你解释为js对象。
然后用户写了<Row><Span>123</Span></Row>
,你就把这个标签转为对应的js对象,然后交给render渲染
纯编译: 作为大聪明的你肯定能想到,我的compiler为什么不一步到位,直接编译成命令式的呢?这样岂不是更快?
至此,框架的三种设计被引出来了。 我们分析一下优劣,纯运行时,只能在运行的时候才知道用户做的什么,平时没办法进行分析。如果假如编译的步骤,那就不一样了,我们可以分析用户提供的内容,进行优化,也可以进行代码提醒等。如果时纯编译,那么确实性能更好,但是有损灵活性,它提供的内容需要编译之后才能使用。
其实业内,三种方向都有人做的,vue选择了中间的方案,加入了大量的优化,性能不输纯编译的Svelte框架。
# 1.5 总结
折衷,确实很牛逼。