我们通常会 影响视图变化的数据 称之为响应式数据
- js的程序性: 一套固定的,不会发生变化的执行流程,所以下边的代码执行结果都是 20
<script>
let product = {
price: 10,
quantity: 2
}
let total = product.price * product.quantity
console.log(total) // 20
product.quantity = 5
console.log(total) // 20
</script>
- 要想让程序变聪明就要让他具备响应性
<script>
let quantity = 2
let product = { // 定一个商品
price: 10,
quantity: quantity
}
let total = 0 // 总价格
let effect = () => { // 计算总价函数
total = product.price * product.quantity
}
Object.defineProperty(product, 'quantity', {
set(newVal){
console.log('setter')
quantity = newVal
effect()
},
get(){
console.log('getter')
return quantity
}
})
</script>
- 由于 javascript 的限制, vue 不能检测数组和对象的变化. vue2 是以 Object.defineProperty 作为核心 API实现响应性,它只可以监听 指定对象的指定属性的 getter 和 setter,如果需要增加响应式的新属性,可以通过 Vue.set方法实现
- 当为 对象 新增一个没有在 data 中声明的属性时, 新增的属性是 不具有响应性的
- 当为 数组 通过下表的形式新增一个元素时, 新增的元素 不具备响应性
- Proxy 对象用于创建一个对象的代理, 从而实现基本操作的拦截和自定义, 语法:
const p = new Proxy(target, handler), taget 可以是任何类型的对象,包括原生数组,函数,甚至是另一个代理
<script>
let product = { // 定一个商品
price: 10,
quantity: 2
}
const proxyProduct = new Proxy(product, {
set(target, key, newVal, receiver){
target[key] = newVal
effect()
return true
}
get(target, key, receiver){
return target[key]
}
})
let total = 0 // 总价格
let effect = () => { // 计算总价函数
total = proxyProduct.price * proxyProduct.quantity
}
</script>
- Proxy 和 Object.definProperty 的区别
- Proxy:
- Proxy 将代理一个对象(被代理对象),得到一个新的对象(代理对象),同时拥有被代理对象中的所有属性
- 当想要修改指定对象的指定属性时,应该使用 代理对象 进行修改
- 代理对象 的任何一个属性都可以触发 handler 的 getter 和 setter
- Object.definProperty
- Object.definProperty 为指定对象的指定属性 设置 属性描述符
- 当要修改对象的指定属性时,可以是使用原对象进行修改
- 通过属性描述符,只有 被监听 的指定属性才可以触发 getter 和 setter
- Proxy:
- proxy 的最佳拍档: Reflect----拦截 js 对象操作
- 当我们期望监听代理对象的 getter 和 setter 时,不应该使用 target[key] ,因为在某些时候他是不可靠的(fullName),而应使用 Reflect ,借助他的 get 和 set 方法,使用 Receiver(proxy 实例) 作为 this,以达到期望的结果(触发三次 getter)
<script>
const p = {
lastName: 'zhang',
firstName: 'san',
get fullName(){
return this.lastName = this.firstName
}
}
const proxy = new Proxy(p, {
get(target, key, receiver){
console.log('getter 被触发')
// return target[key] //走这里只会触发一次,实际上需要触发三次,需要使用 Reflect 方式,最后一个参数 receiver 改变了this的指向
return Reflect.get(target, key, receive)
}
})
console.log(proxy.fullName)
</script>
- 总结:
- Object.definProperty 是 vue2 响应式核心的 API, 但是这个 API 存在一些缺陷,只能监听 指定对象 的 指定属性 的 getter 和 setter ,所以在某些情况下会失去响应性.
- Proxy 是 vue3 响应式核心 API, 该 API 表示代理某一个对象,代理对象将拥有被代理对象的所有属性和方法,并且可以通过操作 代理对象 来监听对应的 getter 和 setter,且为了安全的使用 Proxy ,还需要配合 Reflect 一起使用,因为一旦我们在被代理对象内部,通过 this 来触发 getter 和 setter 时,也是需要被监听到的