Vue3 响应式原理回顾
- Proxy 对象实现属性监听
- 多层属性嵌套时,在访问属性过程中处理下一级属性
- 默认监听动态添加的属性
- 默认监听属性的删除操作
- 默认监听数组索引和 length 属性
- 可以作为单独的模块使用
Proxy 对象
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
严格模式
set 和 deleteProperty 中需要返回布尔类型的值,在严格模式下,如果返回 false 的话会出现 Type Error 的异常;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const target = { foo: 'foo', bar: 'bar' }
const proxy = new Proxy(target, { get (target, key, receiver) { return Reflect.get(target, key, receiver) }, set (target, key, value, receiver) { return Reflect.set(target, key, value, receiver) }, deleteProperty (target, key) { return Reflect.deleteProperty(target, key) } })
proxy.foo = 'foo123' delete proxy.foo
|
receiver
- Proxy 中 receiver:Proxy 或者继承 Proxy 的对象
- Reflect 中 receiver: 如果 target 对象中设置了 getter,getter 中的 this 指向 receiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const obj = { get foo() { console.log(this) return this.bar } } const proxy = new Proxy(obj, { get (target, key) { if (key === 'bar') { return 'value - bar' } return Reflect.get(target, key) } }) console.log(proxy.foo)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const obj = { get foo() { console.log(this) return this.bar } } const proxy = new Proxy(obj, { get (target, key, receiver) { if (key === 'bar') { return 'value - bar' } return Reflect.get(target, key, receiver) } }) console.log(proxy.foo)
|
手写 reactive 函数
reactive
- 接收一个参数,判断参数是否是对象
- 创建拦截器对象 handler,设置 get / set / deleteProperty
- 返回 Proxy 对象
手写实现
reactive 文件夹下新建 index.js 文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| const isObject = val => val !== null && typeof val === 'object' const convert = target => isObject(target) ? reactive(target) : target const hasOwnProperty = Object.prototype.hasOwnProperty const hasOwn = (target, key) => hasOwnProperty.call(target, key)
export function reactive(target) { if (!isObject(target)) return const handler = { get(target, key, receiver) { console.log('get', key) const result = Reflect.get(target, key, receiver) return convert(result) }, set(target, key, value, receiver) { const oldValue = Reflect.get(target, key, receiver) let result = true if (oldValue !== value) { result = Reflect.set(target, key, value, receiver) console.log('set', key, value) } return result }, deleteProperty(target, key) { const hasKey = hasOwn(target, key) const result = Reflect.deleteProperty(target, key) if (hasKey && result) { console.log('delete', key) } return result } } return new Proxy(target, handler) }
|
响应式系统原理 effect && track
在上述 index.js 中继续编写代码:
effect 手写实现
1 2 3 4 5 6 7
| let activeEffect = null export function effect(callback) { activeEffect = callback callback() activeEffect = null }
|
track 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let targetMap = new WeakMap() export function track(target, key) { if (!activeEffect) return let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) }
let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } dep.add(activeEffect) }
|
reactive 中收集依赖调用 track
1 2 3 4 5 6 7
| get(target, key, receiver) { console.log('get', key) track(target, key) const result = Reflect.get(target, key, receiver) return convert(result) }
|
响应式系统原理 trigger
收集依赖结束后,在值修改时,需要触发更新,trigger 函数的作用就在于此
trigger 手写实现
1 2 3 4 5 6 7 8 9 10
| export function trigger(target, key) { const depsMap = targetMap.get(target) if (!depsMap) return const dep = depsMap.get(key) if (dep) { dep.forEach(effect => { effect() }) } }
|
reactive 中触发更新调用 trigger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| set(target, key, value, receiver) { const oldValue = Reflect.get(target, key, receiver) let result = true if (oldValue !== value) { result = Reflect.set(target, key, value, receiver) trigger(target, key) } return result }, deleteProperty(target, key) { const hasKey = hasOwn(target, key) const result = Reflect.deleteProperty(target, key) if (hasKey && result) { trigger(target, key) } return result }
|
响应式系统原理 ref
ref 接收一个原始值或者对象
ref 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export function ref(raw) { if (isObject(raw) && raw.__v_isRef) return let value = convert(raw) const r = { __v_isRef: true, get value() { track(r, 'value') return value }, set value(newValue) { if (newValue !== value) { raw = newValue value = convert(raw) trigger(r, 'value') } } } return r }
|
reactive VS ref
- ref 可以把基本数据类型的数据,转换成响应式对象
- ref 返回的对象,重新赋值成对象也是响应式的
- reactive 返回的对象,重新赋值后丢失响应式
- reactive 返回的对象不可以解构
响应式系统原理 toRefs
接收一个对象作为参数,它会遍历对象身上的所有属性,然后挨个设置成响应式数据
toRefs 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export function toRefs(proxy) { const ret = proxy instanceof Array ? new Array(proxy.length) : {} for(const key in proxy) { ret[key] = toProxyRef(proxy, key) } return ret }
function toProxyRef(proxy, key) { const r = { __v_isRef: true, get value() { return proxy[key] }, set value(newValue) { proxy[key] = newValue } } return r }
|
响应式系统原理 computed
computed 手写实现
1 2 3 4 5
| export function computed(getter) { const res = ref() effect(() => (res.value = getter())) return res }
|