1 )没有响应的数据
// 定义商品对象
const product = {
price: 10,
quantity: 2
}
// 总价格
let total = product.price * product.quantity
console.log(`总价格:${total}`) // 20
// 修改商品的数量
product.quantity = 5
console.log(`总价格:${total}`) // 20
product.quantity
发生改变的时候,最终结果并没有发生变化2 )进一步改造
// 定义商品对象
const product = {
price: 10,
quantity: 2
}
// 总价格
let total = 0
// 定义一个 effect 函数
const effect = () => {
total = product.price * product.quantity // 访问属性,这里是 getter行为
}
effect()
console.log(`总价格:${total}`) // 20
// 修改商品的数量
product.quantity = 5 // 修改属性,这里是 setter 行为
effect() // 注意这里
console.log(`总价格:${total}`) // 50
product.quantity
数据发生改变的时候,手动调用了一次 effect 方法1 )关于响应性数据
2 ) vue2核心响应式API Object.defineProperty() 方法
let quantity = 2
const product = {
price: 10,
quantity
}
// 总价格
let total = 0
// 计算总价格函数
const effect = () => {
total = product.price * product.quantity
}
effect()
console.log(`总价格:${total}`) // 20
// 响应式变化
Object.defineProperty(product, 'quantity', {
set(newVal) {
console.log('setter')
quantity = newVal
effect()
},
get() {
console.log('getter')
return quantity // 这里的变量是暴露在最外面的,不是很好
}
})
3 ) Obeject.defineProperty() 在设计上的缺陷
代码示例,如下
<template>
<div>
<ul>
<li v-for="(val, key, index) in obj" :key="index">
{{ key }} --- {{ val }}
</li>
</ul>
<button @click="addObjKey">为对象增加属性</button>
<div> ---------------- </div>
<ul>
<li v-for="(item, index) in arr" :key="index">
{{ item }} --- {{ index }}
</li>
</ul>
<button @click="addArrItem">为数组增加元素</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
obj: {
name: '张三',
age: 30
},
arr: [
'张三', '李四'
]
}
},
methods: {
addObjKey() {
this.obj.gender = '男'
console.log(this.obj)
},
addArrItem() {
this.arr[2] = '王五'
console.log(this.arr)
}
}
}
</script>
4) Vue3中的 Proxy
文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
语法
const p = new Proxy(target, handler)
代码改造,示例如下
// 定义商品对象
const product = {
price: 10,
quantity: 2
}
// 生成代理对象, 注意事项:使用时不能使用被代理对象(原对象),而应该使用代理对象
// proxy 代理的是整个对象,而非某个对象的某个属性
const proxyProduct = new Proxy(product, {
set(target, key, newVal, receiver) {
// console.log('setter')
target[key] = newVal
// 这里触发 effect 重新计算
effect()
return true
},
get(target, key, receiver) {
// console.log('getter')
return target[key]
}
})
// 总价格
let total = 0
// 定义一个 effect 函数
const effect = () => {
total = proxyProduct.price * proxyProduct.quantity // 访问属性,这里是 getter行为
}
effect()
console.log(`总价格:${total}`) // 20
// 修改商品的数量, 注意这里是修改的代理对象的值,而非被代理对象的值
proxyProduct.quantity = 5 // 修改属性,这里是 setter 行为
effect()
console.log(`总价格:${total}`) // 50
5 ) proxy的最佳合伙API: Reflect, 拦截js对象操作
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。
const obj = { name: '张三' }
Reflect.get(obj, 'name') // '张三'
Reflect.get(target, propertyKey[, receiver])
测试代码如下:
// p1 对象
const p1 = {
lastName: '张',
firstName: '三',
get fullName() {
return this.lastName + this.firstName
}
}
// p2 对象
const p2 = {
lastName: '李',
firstName: '四',
get fullName() {
return this.lastName + this.firstName
}
}
// 测试
console.log(p1.fullName) // 张三
console.log(Reflect.get(p1, 'fullName')) // 张三
console.log(Reflect.get(p1, 'fullName', p2)) // 李四 这里,改变了getter的this指向,this指向了 p2, 所以 getter中获取的是 p2的fullName属性
console.log(p2.fullName) // 李四
使用 proxy 和 Reflect 一起使用
// p1 对象
const p1 = {
lastName: '张',
firstName: '三',
get fullName() {
return this.lastName + this.firstName
}
}
const proxy = new Proxy(p1, {
get(target, key, receiver) {
console.log('getter')
return target[key]
}
})
console.log(proxy.fullName) // 这里进行一次getter操作,会执行一次
// p1 对象
const p1 = {
lastName: '张',
firstName: '三',
get fullName() {
return this.lastName + this.firstName
}
}
const proxy = new Proxy(p1, {
get(target, key, receiver) {
console.log('getter', key)
// return target[key]
return Reflect.get(target, key, receiver) // 注意,修改这里
}
})
console.log(proxy.fullName) // 这里进行一次getter操作,会执行一次
return target[key]
会存在bug,return Reflect.get(target, key, receiver)
代替