前面我们主要学习了框架的基础概念并且完成了自定义vue项目的架子
现在我们来开启任务一,响应系统
什么是响应系统
会影响视图变化的数据称为响应数据,当响应式数据发生变化时,视图也会相应的变化
这就引出了常见面试题:vue2和vue3的响应式实现方式为什么不同:
- 那么vue中的响应式数据是如何实现的呢?
- vue2和vue3实现上有什么变化呢,为什么会变化呢?
Vue2
vue2是以Object.defineProperty()方法实现响应式,该方法会直接在一个对象上定义一个新属性,或者修改一个对象的属性,并返回此对象,以下为商品和价格实现响应性
// 定义一个商品
let quantity=2
let product={
price:10,
quantity:quantity
}
// 总价格
let total=0
// 计算总价格的函数
let effetc=()=>{
total=product.price*product.quantity
}
// 第一次打印
effect()
console.log(`总价格:${total}`)
Object.defineProperty(product,'quantity',{
set(newVal){
console.log('setter')
quantity=newVal
// 触发effect
effect()
},
get(){
console.log('getter')
return quantity
}
}
Object.defineProperty存在一个致命的缺陷:不能检测对象和数组的变化 详细:
- 当为对象新增一个没有在data中声明的属性时,没办法监听对象的变化
- 当为数组新增一个新下标的时候,没办法监听数组的变化
因为object.defineProperty是为指定对象指定属性赋予响应性,而后面新增的属性未实现响应性,能打印出来,但是视图不会变化
Vue3
vue3实现响应性核心api为proxy,该对象可以创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找)
语法:
const p=new Proxy(target,handler)
重写商品实例:
// 定义一个商品
let quantity=2
let product={
price:10,
quantity:quantity
}
const proxyProduct=new Proxy(product,{
set(target,key,newVal,receiver){
// target:被代理对象
// key:使用的对象字段 如 'price'
target[key]=newVal
effect()
return true
},
get(target,key,receiver){
return Reflect.get(target,key,receiver)
}
})
// 总价格
let total=0
// 计算总价格的函数
let effetc=()=>{
total=proxyProduct.price*proxyProduct.quantity
}
// 第一次打印
effect()
console.log(`总价格:${total}`)
为什么需要使用Reflect进行拦截呢
如下代码,我们控制台使用proxyPerson.fullName的时候,只触发了一次get,这肯定不应该吧,因为fullName函数里面还触发了两次,为什么没有打印呢,因为fullName中的this指向的是person,而我们实现响应的this是proxyPerson,所以只触发一次,但是当然不应该只触发一次了。
所以需要指定this,让我们得到期望的触发get三次,所以需要使用Reflect拦截,第三个参数即代表this的指向
const person={
firstName:"John",
lastName:"Doe",
get fullName(){
return this.firstName+" "+this.lastName;
},
}
const proxyPerson=new Proxy(person,{
get(target,key,receiver){
console.log("get",key);
return target[key]
},
})
const proxyPerson2=new Proxy(person,{
get(target,key,receiver){
console.log("get",key);
return Reflect.get(target,key,receiver)
},
})
当我们期望监听代理对象的getter和setter时,不应该使用target[key],因为他在某些时刻,即部分this指向为被代理对象的时候,是不可靠的,应该使用Reflect借助他的get和set方法,使用receiver作为this
区别
proxy
- Proxy将代理一个对象(被代理对象),得到一个新的对象(代理对象),同时拥有被代理对象中所有的属性
- 当想要修改对象的指定属性时,我们应该使用
代理对象进行修改 代理对象的任何一个属性都可以触发handler的getter和setter
Object.defineProperty
Object.defineProperty为指定对象的制定属性设置属性描述符- 当想要修改对象的指定属性时,可以使用原对象进行修改
- 通过属性描述符,只有
被监听的指定属性,才可以触发getter和setter