Vue3 響應(yīng)式
- Proxy 對(duì)象實(shí)現(xiàn)屬性監(jiān)聽
- 多層屬性嵌套,在訪問(wèn)屬性過(guò)程中處理下一級(jí)屬性
- 默認(rèn)監(jiān)聽動(dòng)態(tài)添加的屬性
- 默認(rèn)監(jiān)聽屬性的刪除操作
- 默認(rèn)監(jiān)聽數(shù)組索引和length屬性
- 可以作為單獨(dú)的模塊使用
核心方法
- reactive/ref/toRefs/computed
- effect
- track
- trigger
Proxy
// 問(wèn)題1: set 和 deleteProperty 中需要返回布爾類型的值
// 在嚴(yán)格模式下,如果返回 false 的話會(huì)出現(xiàn) Type Error 的異常
const target = {
foo: 'xxx',
bar: 'yyy'
}
// Reflect.getPrototypeOf()
// Object.getPrototypeOf()
const proxy = new Proxy(target, {
get (target, key, receiver) {
// return target[key]
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
// target[key] = value
return Reflect.set(target, key, value, receiver)
},
deleteProperty (target, key) {
// delete target[key]
return Reflect.deleteProperty(target, key)
}
})
proxy.foo = 'zzz'
// delete proxy.foo
// 問(wèn)題2:Proxy 和 Reflect 中使用的 receiver
// Proxy 中 receiver:Proxy 或者繼承 Proxy 的對(duì)象
// Reflect 中 receiver:如果 target 對(duì)象中設(shè)置了 getter,getter 中的 this 指向 receiver
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)
}
})
reactive
- 接收一個(gè)參數(shù),判斷參數(shù)是否是對(duì)象
- 創(chuàng)建攔截器對(duì)象 handler,設(shè)置 get/set/deleteProperty
- 返回 Proxy 對(duì)象
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) //判斷對(duì)象中是否有對(duì)應(yīng)屬性
export function reactive(target) {
//1.判斷是否是對(duì)象
if (!isObject(target)) return target
const handler = {
get(target, key, receiver) {
//收集依賴
const res = Reflect.get(target, key, receiver)
return convert(res)
},
set(target, key, value, receiver) {
const oldval = Reflect.get(target, key, receiver)
let result = true
if (oldval !== value) {
result = Reflect.set(target, key, value, receiver)
//觸發(fā)更新
}
return result //返回布爾值結(jié)果
},
deleteProperty(target, key) {
const haskey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
if (haskey && result) {
//觸發(fā)更新
}
return result //返回布爾值結(jié)果
}
}
return new Proxy(target, handler)
}
收集依賴 effect && track
export function effect(callback) {
activeEffect = callback
callback() //訪問(wèn)響應(yīng)式對(duì)象屬性,去收集依賴
activeEffect = null
}
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)
}
trigger
//觸發(fā)更新
export function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => {
effect()
});
}
}
創(chuàng)建響應(yīng)式對(duì)象 ref
export function ref(raw) {
//判斷 raw是否是ref創(chuàng)建的對(duì)象 是直接返回
if (isObject(raw) && raw.__v_isRef) return
let value = convert(raw) //判斷是否是對(duì)象 是的話 創(chuàng)建reactive 響應(yīng)式對(duì)象
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')
}
}
}
}
reactive ref
- ref 可以把基本數(shù)據(jù)類型數(shù)據(jù),轉(zhuǎn)成響應(yīng)式對(duì)象
- ref 返回的對(duì)象,重新賦值成對(duì)象也是響應(yīng)式的
- reactive 返回的對(duì)象,重新賦值丟失響應(yīng)式
- reactive 返回的對(duì)象不可以解構(gòu)
toRefs函數(shù)
接收一個(gè)reactive返回的響應(yīng)式對(duì)象,也就是一個(gè)proxy對(duì)象,傳入?yún)?shù)不是reactive創(chuàng)建的響應(yīng)式對(duì)象直接返回,再把傳入對(duì)象的所有屬性轉(zhuǎn)換成一個(gè)類似ref返回的對(duì)象,把轉(zhuǎn)換后的屬性掛載在一個(gè)新的對(duì)象上返回。
function toProxyRef(proxy, key) {
const r = {
__v_isRef: true,
get value() {
return proxy[key]
},
set value(newValue) {
proxy[key] = newValue
}
}
return r
}
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
}
computed
需要接收一個(gè)有返回值的函數(shù)作為參數(shù),這個(gè)函數(shù)的返回值就是計(jì)算屬性的值,并且需要監(jiān)聽這個(gè)函數(shù)內(nèi)部使用響應(yīng)數(shù)據(jù)的變化,最后將這個(gè)函數(shù)執(zhí)行的結(jié)果返回。
export function computed(getter){
const result = ref()
effect( () => ( result.value = getter() ) )
return result
}
所有方法
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) //判斷對(duì)象中是否有對(duì)應(yīng)屬性
export function reactive(target) {
//1.判斷是否是對(duì)象
if (!isObject(target)) return target
const handler = {
get(target, key, receiver) {
//收集依賴
track(target, key)
const res = Reflect.get(target, key, receiver)
return convert(res)
},
set(target, key, value, receiver) {
const oldval = Reflect.get(target, key, receiver)
let result = true
if (oldval !== value) {
result = Reflect.set(target, key, value, receiver)
//觸發(fā)更新
trigger(target, key)
}
return result //返回布爾值結(jié)果
},
deleteProperty(target, key) {
const haskey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
if (haskey && result) {
//觸發(fā)更新
trigger(target, key)
}
return result //返回布爾值結(jié)果
}
}
return new Proxy(target, handler)
}
let activeEffect = null
export function effect(callback) {
activeEffect = callback
callback() //訪問(wèn)響應(yīng)式對(duì)象屬性,去收集依賴
activeEffect = null
}
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)
}
//觸發(fā)更新
export function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effect => {
effect()
});
}
}
export function ref(raw) {
//判斷 raw是否是ref創(chuàng)建的對(duì)象 是直接返回
if (isObject(raw) && raw.__v_isRef) return
let value = convert(raw) //判斷是否是對(duì)象 是的話 創(chuàng)建reactive 響應(yīng)式對(duì)象
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')
}
}
}
}
function toProxyRef(proxy, key) {
const r = {
__v_isRef: true,
get value() {
return proxy[key]
},
set value(newValue) {
proxy[key] = newValue
}
}
return r
}
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
}
export function computed(getter) {
const result = ref()
effect(() => (result.value = getter()))
return result
}