《Vue设计与实现》笔记(持续更新)

一、框架设计概览

声明式与命令式(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个例子

  1. 提供友好的警告信息,比如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'
}

响应式数据是指:

  1. 能够捕获该数据的变更。
  2. 该数据变更后能自动调用更新依赖于它的副作用函数

响应式数据的基本实现

如何才能让 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个例子

  1. 提供友好的警告信息,比如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'
}

响应式数据是指:

  1. 能够捕获该数据的变更。
  2. 该数据变更后能自动调用更新依赖于它的副作用函数

响应式数据的基本实现

如何才能让 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函数一种硬编码的方式调用。

设计一个完善的响应系统

你可能感兴趣的:(vue3)