Vuejs设计与实现
第一章、权衡艺术
1.1 命令式和声明式
- 本节强调了原生的方法操作DOM和JQuery方法操作DOM的方式都是命令式。而像Vue则是一个声明式。
- 两者区别,命令式则是关注过程,声明式则关注结果。
1.2 性能与可维护性的权衡
-
本节抛出一个结论:声明式代码的性能不优于命令式的性能。
-
两者对比。基于直接修改的性能消耗定义为A,找出差异的性能消耗定义为B。
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = B + A
-
正常看来,整找出差异的性能消耗为0时。声明式代码和命令式代码的性能相同,无法做到超越。毕竟总的来说框架本身就是封装了命令式代码才实现了面向用户的声明式。
-
框架设计者的原则:在保持可维护的同时让性能损失最小化。
1.3 虚拟DOM的性能到底如何
-
-
对比纯JavaScript操作与DOM操作
- 文中给出上述跑分例子,上方则是循环10000次,每次创建一个JavaScript对象并将其添加到数组中。下方则是DOM操作,每次创建一个DOM元素将其添加到页面中。因此我们可以发现,纯JavaScript层面的操作要比DOM操作快得多。
- innerHtml创建页面的性能: HTML字符串拼接的计算量 + innerHTML的DOM计算量。
-
-
-
虚拟DOM
- 虚拟DOM : 创建JavaScript的计算量 % 创建真实DOM的计算量
- innerHTML: innerHTML更新页面的过程是重新构建HTML字符串,再重新设置DOM元素的innerHTML,也就是说,哪怕我们只更改了一个文字,也要重新设置innerHTML属性。而重新设置innerHTML属性就等价于 销毁所有旧的DOM元素,再全量创建新的DOM元素。
- 虚拟DOM: 它需要重新创建JavaScript对象(虚拟DOM树),然后比较新旧DOM找到变化的元素并更新他们。
-
-
虚拟DOM的优势
- 虚拟DOM在更新页面时只会更新必要的元素,但innerHTML需要全量更新。
1.4 运行时和编译时
1.4.1. vue是怎么渲染的
本节讲述了Vue框架,用户构建一个树型的数据对象,根据运行Render函数来对该对象递归地将数据渲染成DOM元素。
const obj = {
tag: "div",
children: [
{ tag: 'span', children: "hello world"}
]
}
根据上述代码,每个对象有两个属性: tag代表标签名名称,children既可以是一个数组(代表子节点),也可以直接是一段文本(代表文本子节点)
1.4.2. 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 {
// 数组, 递归调用 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 下
Reder(obj, document.body)
痛点: 用户觉得手动写一个树型的对象太难了。
1.4.3. 实现Compiler函数
// 调用 Compiler 编译得到树型结构的数据对象
const obj = Compiler(html)
// 再调用 Render 进行渲染
Render(obj,document.obj)
1.4.4. 思考: 为什么不直接编译成命令式代码
- 由于纯运行时框架,由于它没有编译的过程,因此我们没有办法分析用户提供的内容。但,但是如果加入编译步骤,就可以对用户提供的内容进行提取,并进一步操作。
- 如果是纯运行框架,那么需要编译完成后才能看到结果。(Svelte纯运行时)
END
个人博客汇总地址:github.com/codehzy/blo…