# Vue.js 设计与实现
# 权衡的艺术
声明试 命令式的区别
为什么需要虚拟 DOM
运行时 纯编译 运行时+纯编译
# 设计框架的核心要素
# Vue.js 3 的设计思路
# 3.2 渲染器
# 手写渲染器
// <div click="alert(123)">123</div>
const VNode = {
tag: 'div',
props{
onClick: () => alert('123')
},
children:'123'
}
function render(VNode,container){
}
# 3.3 组件的本质:一组 DOM 元素的封装
# 3.4 模板的工作原理
# 编译器
编译器的作用其实就是将模板编译为渲染函数
- h 函数返回虚拟 dom
# 渲染器/编译器
编译器和渲染器之间是存在信息交流的,它们互相配合使得性能进一步提升,而它们之间交流的媒介就是虚拟 DOM 对象。
# 4.响应式系统
# proxy (opens new window)
const p = new Proxy(target, handler)
# handler 对象的方法
- get(target, property, receiver) 属性的获取
- set(target, property, value, receiver) 属性的设置
# 响应式
读取收集副作用 设置触发副作用
没有在副作用函数与被操作的目标字段之间建立明确的联系
- 使用 WakeMap 作为外层 bucket
- 属性内部还是使用 Set 存好每一个 属性对应的副作用函数
在代理函数 hander 里的读取时,调用 track 函数 收集 对象属性对应的副作用 在代理函数 hander 里的设置时,调用 trigger 函数 触发对象属性对应的副作用
# 调度执行
可调度性是响应系统非常重要的特性。首先我们需要明确什么是可调度性。所谓可调度,指的是当 trigger 动作触发副作用函数重新执行时,有能力决定副作用函数执行的时机、次数以及方式。
- 作用:Vue.js 中连续多次修改响应式数据但只会触发一次更新,实际上 Vue.js 内部实现了一个更加完善的调度器
# 计算属性 computed 与 lazy
最核心的改动是使用 lazy 选项创建了一个懒执行的 effect
function computed(getter) {
let value
let dirty = true
const effectFn = effect(getter, {
lazy: true,
scheduler() {
if (!dirty) {
dirty = true
// 当计算属性依赖的响应式数据变化时,手动调用 trigger 函数触发响应
trigger(obj, 'value')
}
},
})
const obj = {
get value() {
if (dirty) {
value = effectFn()
dirty = false
}
// 当读取 value 时,手动调用 track 函数进行追踪
track(obj, 'value')
return value
},
}
return obj
}
# watch 的实现原理
watch 的实现本质上就是利用了 effect 以及 options.scheduler 选项,
function watch(source, cb) {
let getter
if (typeof source === 'function') {
getter = source
} else {
getter = () => traverse(source)
}
// 定义旧值与新值
let oldValue, newValue
// 使用 effect 注册副作用函数时,开启 lazy 选项,并把返回值存储到 effectFn 中以便后续手动调用
const effectFn = effect(() => getter(), {
lazy: true,
scheduler() {
// 在 scheduler 中重新执行副作用函数,得到的是新值
newValue = effectFn()
// 将旧值和新值作为回调函数的参数
cb(newValue, oldValue)
// 更新旧值,不然下一次会得到错误的旧值
oldValue = newValue
},
})
// 手动调用副作用函数,拿到的值就是旧值
oldValue = effectFn()
}
# 非原始值的响应式方案
# Proxy 只能够代理对象的基本语义很重要
# 基本语义
对象的基本操作:{name:123} obj.name fn()存在
# reflect 能够在代理函数的时候,确保 this 指向的正确性
# diff
# 简单 diff
当新旧 vnode 的子节点都是一组节点时,为了以最小的性能开销完成更新操作,需要比较两组子节点,用于比较的算法就叫作 Diff 算法。
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
</ul