前言
本博文用于记录学习Vue 3响应式原理课程的学习笔记。 Vue 3响应式原理 Vue 3响应式原理学习笔记(一)
笔记
Proxy and Reflect
- 如何让我们的响应式引擎自动调用跟踪和触发?
- 解决方法:
- 运行effect时,如果访问了产品的属性,或者说是使用了GET时,调用track去保存effect
- 如果产品的属性改变了,或者说使用了SET,调用trigger来运行那些保存了的effect
- 解决方法:
如何拦截GET和SET
- VUE2中使用了ES5的Object.defineProperty()去拦截GET和SET
- VUE3中使用了ES6 Reflect和 Proxy去拦截(VUE3原生不支持Internet Explore)
- Proxy是另一个对象的占位符,默认情况下对该对象进行委托
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {})
console.log(proxiedProduct.quantity) //先调用Proxy,Proxy调用product,返回product到console log,最后打印出 2,简单来说就是一个对象委托
new Proxy(对象, handler) //handler处理程序,一个对象,可以传递一个trap(诱捕器),这个trap可以让我们拦截基本操作,如属性查找或枚举或函数调用
- 使用handler的实例
// 使用handler
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
get() {
console.log('Get was called')
return 'Not the value'
}
})
console.log(proxiedProduct.quantity) //Get was called Not the value
// handler加入参数
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
get(target, key) {
console.log('Get was called with key = ' + key)
return target[key]
}
})
console.log(proxiedProduct.quantity)
// 在Proxy里使用Reflect
let product = { price: 5, quantity: 2}
let proxiedProduct = new Proxy(product, {
get(target, key, receiver) { //receiver保证了当我们的对象有继承自其它对象的值或函数,它保证了当我们的对象有继承自其它对象的值或函数时,this指针能正确的指向使用(的对象),可以避免一些我们在Vue2中的响应式警告
console.log('Get was called with key = ' + key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('Set was called with key = ' + key + ' and value = ' + value)
return Reflect.set(target, key, value, receiver)
},
})
proxiedProduct.quantity = 4
console.log(proxiedProduct.quantity)
- Vue3对handler的封装
function reactive(target) {
const handler = {
get(target, key, receiver) {
console.log('Get was called with key = ' + key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('Set was called with key = ' + key + ' and value = ' + value)
return Reflect.set(target, key, value, receiver)
}
}
return new Proxy(target, handler)
}
let product = reactive({ price: 5, quantity: 2 })
product.quantity = 4
console.log(product.quantity)
// Set was called with key = quantity and value = 4
// Get was called with key = quantity
// 4
- 因此Vue3是将对象传递到响应式函数中,响应式函数便会返回一个代理对象
- 而我们便会将这个代理对象当作原始对象来使用
如何监听GET和SET
reactive函数
function reactive(target) {
const handler = {
get(target, key, receiver) {
let result = Reflect.get(target, key, receiver)
track(target, key)
return result
}
set(target, key, value, receiver) {
let oldValue = target[key]
let result = Reflect.set(target, key, value, receiver)
if (oldValue != value) {
trigger(target, key)
}
return result
},
}
return new Proxy(target, handler)
}
let product = reactive({ price: 5, quantity: 2 })
过程
// reactive函数返回产品对象的proxy(代理)
let product = reactive({ price: 5, quantity: 2 })
let total = 0
let effect = () => {
total = product.price * product.quantity
}
// 运行effect函数,因为get了product对象,所以会调用到track函数,而track函数会给产品创建一个新的映射
effect()
console.log(total)
product.quantity = 3
console.log(total)
- 将对象传给reactive函数,而reactive函数会返回一个proxy对象,这个proxy对象自带拦截GET(访问对象属性)与SET(改变对象属性)
- 拦截到GET后,proxy会调用track(对象,属性名)函数 — 保存
- 在targetMap中,它会给对象创建一个新的映射,值(value)是一个新的deps图,即映射对象属性到一个新的deps
- deps就是一个effects集(Set)
- 拦截到SET后