Vue3 使用 Proxy 對象重寫響應式系統(tǒng),這個系統(tǒng)主要有以下幾個函數(shù)來組合完成的:
1、reactive:
接收一個參數(shù),判斷這參數(shù)是否是對象。不是對象則直接返回這個參數(shù),不做響應式處理
創(chuàng)建攔截器對象 handler, 設(shè)置 get/set/deleteProperty
get
收集依賴(track)
返回當前 key 的值。
如果當前 key 的值是對象,則為當前 key 的對象創(chuàng)建攔截器 handler, 設(shè)置 get/set/deleteProperty
如果當前的 key 的值不是對象,則返回當前 key 的值
set
設(shè)置的新值和老值不相等時,更新為新值,并觸發(fā)更新(trigger)
deleteProperty
當前對象有這個 key 的時候,刪除這個 key 并觸發(fā)更新(trigger)
返回 Proxy 對象
2、effect: 接收一個函數(shù)作為參數(shù)。作用是:訪問響應式對象屬性時去收集依賴
3、track:
接收兩個參數(shù):target 和 key
如果沒有 activeEffect,則說明沒有創(chuàng)建 effect 依賴
如果有 activeEffect,則去判斷 WeakMap 集合中是否有 target 屬性,
WeakMap 集合中沒有 target 屬性,則 set(target, (depsMap = new Map()))
WeakMap 集合中有 target 屬性,則判斷 target 屬性的 map 值的 depsMap 中是否有 key 屬性
depsMap 中沒有 key 屬性,則 set(key, (dep = new Set()))
depsMap 中有 key 屬性,則添加這個 activeEffect
4、trigger:
判斷 WeakMap 中是否有 target 屬性
WeakMap 中沒有 target 屬性,則沒有 target 相應的依賴
WeakMap 中有 target 屬性,則判斷 target 屬性的 map 值中是否有 key 屬性,有的話循環(huán)觸發(fā)收集的 effect()
//判斷是否是對象
const isObject = (val) => val != null && typeof val === 'object'
//判斷是否是對象,如果是對象繼續(xù)調(diào)用reactive轉(zhuǎn)換成響應式對象
const convert = (target) => (isObject(target) ? reactive(target) : target)
const hasOwnProperty = Object.prototype.hasOwnProperty
//判斷對象中是否有某個屬性
const hasOwn = (target, key) => hasOwnProperty.call(target)
##手寫核心響應式原理
/**
* 接收一個參數(shù),判斷這個參數(shù)是否是對象
* 創(chuàng)建攔截器對象handler,設(shè)置get/set/deleteProperty
* 返回Proxy對象
*/
export function reactive(target) {
//判斷target是否是對象,不是對象直接返回
if (!isObject(target)) return
//創(chuàng)建攔截器對象handler
const handler = {
get(target, key, receiver) {
//收集依賴
track(target, key)
const result = Reflect.get(target, key, receiver)
//如果result是對象繼續(xù)調(diào)用reactive轉(zhuǎn)換成響應式對象
return convert(result)
},
set(target, key, value, receiver) {
//獲取屬性值
const oldValue = Reflect.get(target, key, receiver)
let result = true
//判斷新值是否等于舊值
if (oldValue !== value) {
// 如果相等,調(diào)用set修改屬性值
result = Reflect.set(target, key, value, receiver)
//觸發(fā)更新
trigger(target, key)
}
return result
},
deleteProperty(target, key) {
//判斷target中是否有自己的key屬性
const hasKey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
if (hasKey && result) {
//target中有key屬性且刪除成功
//觸發(fā)更新
trigger(target, key)
}
return result
},
}
//返回代理對象
return new Proxy(target, handler)
}
let activeEffect = null
//接收一個函數(shù)作為參數(shù)
export function effect(callback) {
activeEffect = callback
callback() //訪問響應式對象屬性,去收集依賴
activeEffect = null
}
//定義一個map 存儲依賴
let targetMap = new WeakMap()
//接收兩個參數(shù) 目標對象target 要跟蹤的屬性key
export function track(target, key) {
if (!activeEffect) return
let depsMap = targetMap.get(target)
//判斷depsMap 是否有值,沒有的話就創(chuàng)建一個,存儲到targetMap
if (!depsMap) {
targetMap.set(target, (depsMap = new map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
//發(fā)送通知函數(shù)
export function trigger(target, key) {
//去depsMap中查找target
const depsMap = targetMap.get(target)
//如果沒有找到直接返回
if (!depsMap) return
//查找dep集合
const dep = depsMap.get(key)
if (dep) {
//找到dep中的每一個函數(shù),調(diào)用effect
dep.forEach((effect) => {
effect()
})
}
}
//定義一個響應式屬性
export function ref(raw) {
//判斷raw是否是ref創(chuàng)建的對象, 如果是直接返回
if (isObject(raw) && raw.__v__isRef) return
let value = convert(raw)
//創(chuàng)建ref對象
const r = {
__v__isRef: true,
get value() {
//收集依賴
track(r, 'value')
return value
},
set value(newValue) {
if (newValue !== value) {
raw = newValue
value = convert(raw)
//觸發(fā)更新
trigger(r, 'value')
}
},
}
return r
}
export function toRefs(proxy) {
//判斷proxy 是否是reactive創(chuàng)建的
//判斷proxy是否是數(shù)組
const ret = proxy instanceof Array ? new Array(proxy.length) : {}
for (const key in proxy) {
//將每一個屬性都轉(zhuǎn)換成ref屬性
ret[key] = toProxyRef(proxy, key)
}
return ret
}
//將屬性轉(zhuǎn)換成ref
function toProxyRef(proxy, key) {
const r = {
__v__isRef: true,
get value() {
//無需收集依賴, proxy對象內(nèi)部會收集
return proxy[key]
},
set value(newValue) {
proxy[key] = newValue
},
}
return r
}
//computed 需要接收一個有返回值函數(shù)作為參數(shù)
//這個函數(shù)的返回值就是計算屬性的值
export function computed(getter) {
const result = ref()
//將getter的返回值存入result中
effect(() => {
result.value = getter()
})
return result
}