这是我参与「第四届青训营 」笔记创作活动的的第20天
我将创作一些 Vue.js 学习相关的笔记。
《Vue.js设计与实现》笔记 Vol. 1
框架设计概览
权衡的艺术
命令式和声明式
命令式:
- 更加关注过程
- 理论上可以做到极致优化
- 用户要承受巨大的心智负担
声明式:
- 更加关注结果
- 性能上有一定的牺牲
- 能够有效减轻用户的心智负担
性能与可维护性的权衡
声明式代码的性能不优于命令式代码的性能,因为声明式代码会比命令式代码多处找出差异的性能消耗。
框架本身就是封装了命令式代码才实现了面向用户的声明式。
Vue.js 选择声明式的设计方案的原因:声明式代码的可维护性更强。
框架设计者要做的是:在保持可维护性的同时让性能损失最小化。
虚拟 DOM 的性能
声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗
虚拟 DOM 的意义在于使找出差异的性能消耗最小化
更新页面时的性能对比:
-
innerHTML(模板)
- 心智负担中等
- 性能差
-
虚拟 DOM
- 心智负担小
- 可维护性强
- 性能不错
-
原生 JavaScript
- 心智负担大
- 可维护性差
- 性能高
运行时和编译时
纯运行时框架
树型结构的数据对象:
const obj = {
tag: 'div',
children: [
{ tag: 'span', children: 'hello world' }
]
}
Render 函数:
function Render(obj, root) {
const el = document.createElement(obj.tag)
if (typeof obj.children === 'string') {
const text = document.createTextNode(obj.children)
el.appendChild(text)
} else if (obj.children) {
// array,递归调用 Render,使用 el 作为 root 参数
obj.children.forEach((child) => Render(child, el))
}
// 将元素添加到 root
root.appendChild(el)
}
使用方法:
const obj = {
tag: 'div',
children: [
{ tag: 'span', children: 'hello world' }
]
}
// 渲染到 body 下
Render(obj, document.body)
编译时 + 运行时框架
编写一个 Compiler 函数
用户分别调用 Compiler 函数和 Render 函数:
const html = `
<div>
<span>hello world</span>
</div>
`
// 调用 Compiler 编译得到树型结构的数据对象
const obj = Compiler(html)
// 再调用 Render 进行渲染
Render(obj, document.body)
纯编译时框架
只需要 Compiler 函数,将 HTML 字符串编译为命令式代码
const div = document.createElement('div')
const span = document.createElement('span')
span.innerText = 'hello world'
div.appendChild(span)
document.body.appendChild(div)
总结
纯运行时的框架没有编译的过程,因而不能分析用户提供的内容;加入编译步骤,可以分析用户提供的内容,看看哪些内容未来可能改变,哪些内容永远不会改变,这样就可以在编译时提取这些信息,然后将其传给 Render 函数,Render 函数得到这些信息之后,可以做进一步的优化;纯编译时的框架直接将用户提供的内容编译成可执行的 JavaScript 代码,因而性能可能会更好,但有损灵活性,因为内容必须编译后才能用。
Svelte 是纯编译时的框架。
Vue.js 3 是一个编译时 + 运行时的框架,它在保持灵活性的基础上,还能通过编译手段分析用户提供的内容,从而进一步提升更行性能。
引用参考
霍春阳. Vue.js设计与实现[M]. 1. 北京:人民邮电出版社, 2022.