一、框架设计概览
声明式与命令式(Declarative vs Imperative)
声明式和命令式是两种编程范式。vue/react
是声明式的,jquery那样直接操作dom是命令式
Alright here’s a metaphor.
Declarative Programming is like asking your friend to draw a landscape. You don’t care how they draw it, that’s up to them.
Imperative Programming is like your friend listening to Bob Ross tell them how to paint a landscape. While good ole Bob Ross isn’t exactly commanding, he is giving them step by step directions to get the desired result.
声明式就像你告诉你朋友画一幅画,你不用去管他怎么画的细节
命令式就像按照你的命令,你朋友一步步把画画出来
换言之
- 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
- 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
声明式与命令式的差异: 命令式更加注重过程,声明式注重结果。命令式理论上可以做到极致优化,但用户承受巨大的心智负担。声明式则牺牲一定的性能,换取代码可维护性,减轻用户的心智负担。
性能与可维护性的权衡
结论: 声明式代码的性能不优于命令式代码的性能
如果把直接修改的性能消耗定义为A,把找出差异的性能消耗定义为B,那么:
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = A + B
Q: Vue为什么要采用声明式的设计方案?
A:消耗一定的性能来换取更优的代码可维护性。
设计者要做到的宗旨:
在保持可维护性的同时让性能损失最小化。
虚拟DOM的性能如何
明确虚拟DOM要解决的问题:使找出差异的性能消耗最小化
运行时和编译时
- 纯运行时: 没有编译过程,没法分析内容差异进行优化
- 运行时+编译时(VUE):HTML模板 --> 编译器 --> 数据对象(虚拟DOM)--> 渲染器 --> 渲染DOM
- 纯编译时(Svelte): HTML模板 --> 编译器 --> DOM命令式代码,性能可能更好,但不够灵活
提高用户的开发体验
作者举了2个例子
- 提供友好的警告信息,比如vue中的
warn
函数。它会收集当前发生错误的组件栈信息,并向用户提供有用的信息。
warn("xxxx")
2. 自定义formatter函数,实现在开发环境中输出自定义的数据格式
控制框架代码体积
框架应该输出怎么样的构建产物
特性开关
错误处理
良好Typescript支持
Vue3的设计思路
声明式的描述 UI
Vue 2种描述UI的方式:
- 模板
- 对象
Q: 二者有何不同?
A: 使用JavaScript对象描述UI更加灵活
初识渲染器
Q:什么是虚拟dom?
A: 虚拟DOM就是用JavaScript对象描述真实DOM结构Q:什么是渲染器?
A:把虚拟DOM渲染为真实DOM
组件的本质
虚拟dom除了能描述真实dom之外,还能描述组件。
Q: 组件的本质是什么?
A: 组件就是一组dom元素的封装
模板工作原理
Q: 什么是编译器
二、响应式系统
响应式系统的作用与实现
响应式数据与副作用函数
副作用函数指的是会产生副作用的函数。
function effect() {
document.body.innerText = 'hello vue3'
}
响应式数据是指:
- 能够捕获该数据的变更。
- 该数据变更后能自动调用更新依赖于它的副作用函数
响应式数据的基本实现
如何才能让 obj 变成响应式数据呢?
- 当副作用函数 effect 执行时,会触发字段 obj.text 的读取操作;
- 当修改 obj.text 的值时,会触发字段 obj.text 的设置操作。
所以基本思路就是拦截一个对象的读取和设置操作,当读取字段obj.text 时,我们可以把副作用函数 effect 存储到一个“桶”里。接着,当设置 obj.text 时,再把副作用函数 effect 从“桶”里取出并执行即可。
在ES2015 之前,只能通过Object.defineProperty
函数实现,这也是 Vue.js 2 所采用的方式。在 ES2015+ 中,我们可以使用代理对象Proxy
来实现,这也是 Vue.js 3 所采用的方式。
简单例子:
// 存储副作用函数的桶
const bucket = new Set()
// 原始数据
const data = { text: "hello world" }
// 对原始数据的代理
const data = new Proxy(data, {
// 拦截读取操作
get(target, key)=>{
// 将副作用函数effect添加到桶中
bucket.add(effect)
// 返回属性值
return target[key]
},
set(target, key, newVal) {
// 设置值
target[key] = newVal;
// 把副作用从桶里提出并执行
bucket.forEach(fn => fn())
// 返回true代表操作成功
return true;
}
})
// 副作用函数
function effect() {
document.body.innerText = data.text
}
// 执行副作用函数触发读取
effect();
// 1s后修改响应式数据
setTimeout(() =>{
obj.text = 'hello vue3'
}, 1000)
当然存在很多缺陷,比如effect函数一种硬编码的方式调用。
设计一个完善的响应系统
一、框架设计概览
声明式与命令式(Declarative vs Imperative)
声明式和命令式是两种编程范式。vue/react
是声明式的,jquery那样直接操作dom是命令式
Alright here’s a metaphor.
Declarative Programming is like asking your friend to draw a landscape. You don’t care how they draw it, that’s up to them.
Imperative Programming is like your friend listening to Bob Ross tell them how to paint a landscape. While good ole Bob Ross isn’t exactly commanding, he is giving them step by step directions to get the desired result.
声明式就像你告诉你朋友画一幅画,你不用去管他怎么画的细节
命令式就像按照你的命令,你朋友一步步把画画出来
换言之
- 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
- 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
声明式与命令式的差异: 命令式更加注重过程,声明式注重结果。命令式理论上可以做到极致优化,但用户承受巨大的心智负担。声明式则牺牲一定的性能,换取代码可维护性,减轻用户的心智负担。
性能与可维护性的权衡
结论: 声明式代码的性能不优于命令式代码的性能
如果把直接修改的性能消耗定义为A,把找出差异的性能消耗定义为B,那么:
- 命令式代码的更新性能消耗 = A
- 声明式代码的更新性能消耗 = A + B
Q: Vue为什么要采用声明式的设计方案?
A:消耗一定的性能来换取更优的代码可维护性。
设计者要做到的宗旨:
在保持可维护性的同时让性能损失最小化。
虚拟DOM的性能如何
明确虚拟DOM要解决的问题:使找出差异的性能消耗最小化
运行时和编译时
- 纯运行时: 没有编译过程,没法分析内容差异进行优化
- 运行时+编译时(VUE):HTML模板 --> 编译器 --> 数据对象(虚拟DOM)--> 渲染器 --> 渲染DOM
- 纯编译时(Svelte): HTML模板 --> 编译器 --> DOM命令式代码,性能可能更好,但不够灵活
提高用户的开发体验
作者举了2个例子
- 提供友好的警告信息,比如vue中的
warn
函数。它会收集当前发生错误的组件栈信息,并向用户提供有用的信息。
warn("xxxx")
2. 自定义formatter函数,实现在开发环境中输出自定义的数据格式
控制框架代码体积
框架应该输出怎么样的构建产物
特性开关
错误处理
良好Typescript支持
Vue3的设计思路
声明式的描述 UI
Vue 2种描述UI的方式:
- 模板
- 对象
Q: 二者有何不同?
A: 使用JavaScript对象描述UI更加灵活
初识渲染器
Q:什么是虚拟dom?
A: 虚拟DOM就是用JavaScript对象描述真实DOM结构Q:什么是渲染器?
A:把虚拟DOM渲染为真实DOM
组件的本质
虚拟dom除了能描述真实dom之外,还能描述组件。
Q: 组件的本质是什么?
A: 组件就是一组dom元素的封装
模板工作原理
Q: 什么是编译器
二、响应式系统
响应式系统的作用与实现
响应式数据与副作用函数
副作用函数指的是会产生副作用的函数。
function effect() {
document.body.innerText = 'hello vue3'
}
响应式数据是指:
- 能够捕获该数据的变更。
- 该数据变更后能自动调用更新依赖于它的副作用函数
响应式数据的基本实现
如何才能让 obj 变成响应式数据呢?
- 当副作用函数 effect 执行时,会触发字段 obj.text 的读取操作;
- 当修改 obj.text 的值时,会触发字段 obj.text 的设置操作。
所以基本思路就是拦截一个对象的读取和设置操作,当读取字段obj.text 时,我们可以把副作用函数 effect 存储到一个“桶”里。接着,当设置 obj.text 时,再把副作用函数 effect 从“桶”里取出并执行即可。
在ES2015 之前,只能通过Object.defineProperty
函数实现,这也是 Vue.js 2 所采用的方式。在 ES2015+ 中,我们可以使用代理对象Proxy
来实现,这也是 Vue.js 3 所采用的方式。
简单例子:
// 存储副作用函数的桶
const bucket = new Set()
// 原始数据
const data = { text: "hello world" }
// 对原始数据的代理
const data = new Proxy(data, {
// 拦截读取操作
get(target, key)=>{
// 将副作用函数effect添加到桶中
bucket.add(effect)
// 返回属性值
return target[key]
},
set(target, key, newVal) {
// 设置值
target[key] = newVal;
// 把副作用从桶里提出并执行
bucket.forEach(fn => fn())
// 返回true代表操作成功
return true;
}
})
// 副作用函数
function effect() {
document.body.innerText = data.text
}
// 执行副作用函数触发读取
effect();
// 1s后修改响应式数据
setTimeout(() =>{
obj.text = 'hello vue3'
}, 1000)
当然存在很多缺陷,比如effect函数一种硬编码的方式调用。