1. proxy的优点
- Proxy对象实现属性监听
- 多层嵌套,在访问属性过程中处理下一级属性
- 默认监听动态添加的属性
- 默认监听属性的删除操作
- 默认监听数组的索引和length属性
- 可以作为单独模块使用
2. Vue3响应式API的模拟实现
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)
function reactive(target) {
if (!isObject(target)) return target
const handler = {
get(target, key, receiver) {
track(target, 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)
trigger(target, key)
}
return result
},
deleteProperty(target, key) {
const hadKey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
if (hadKey && result) {
trigger(target, key)
}
return result
},
}
return new Proxy(target, handler)
}
let activeEffect = null
function effect(callback) {
activeEffect = callback
callback()
activeEffect = null
}
let targetMap = new WeakMap()
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)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach((effect) => {
effect()
})
}
}
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
}
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
}
function computed(getter) {
const result = ref()
effect(() => (result.value = getter()))
return result
}