使用 Object
构造函数上的 defineProperty()
实现。
1、vue2存在的问题
注意:如果要解决上述问题
① 使用 vue
实例对象上的 $set
进行添加或修改,$delete
进行删除。
② 使用 Vue.set
进行添加或修改,Vue.delete
进行删除。
③ 使用 vue
实例对象上的 $nextTick
进行页面更新。
④ 使用数组的一些方法对数组进行操作(如 splice()
)。
2、实现响应式的原理
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。(1)Object.defineProperty()
的基本使用
Object.defineProperty("对象", "属性", {
value: 0, // 属性值
enumerable: true, // 属性是否可被枚举,默认 false
writable: true, // 属性是否可被修改,默认 false
configurable: true, // 属性是否可被删除,默认 false
get() {}, // 获取属性值时调用,此函数需返回属性的属性值
set(value) {}, // 修改属性值时调用,value为修改后的值
})
(2)vue2 如何实现响应式
let person1 = {
name: "张三",
sex: "男",
age: 18
}
let person2 = {
name: "李芳",
sex: "女"
}
Object.defineProperty(person2, "age", {
get() {
return person1.age
},
set(value) {
person1.age = value
},
})
console.log(person2.age) // 18
person1.age = 20
console.log(person2.age) // 20
person2.age = 30
console.log(person1.age) // 30
// 向 person2 对象上添加 age 属性
// 在修改 person2 的 age 时,set() 修改的是 person1 的 age
// 在获取 person2 的 age 时,get() 返回的是 person1 的 age
// defineProperty() 将 person1 和 person2 关联起来,实现了响应式。
使用 window
上的内置构造函数 Proxy
实现。
1、实现原理
Proxy
(代理) :拦截对象中任意属性的变化,包括属性值的读取、修改,属性的添加、删除。Reflect
(反射):对源对象的属性进行操作。(1)vue3 如何实现响应式
const person = {
name: "张三",
sex: "男",
age: 18
}
// Proxy 构造函数接收两个参数,第一个为被代理的源对象,第二个为配置项
// 下述 p 为代理对象,person 为被代理的源对象
const p = new Proxy(person, {
// 在获取源对象中属性的属性值时调用
// target 为源对象,propName 为要获取的属性名
// 此函数需返回属性的属性值
get(target, propName) {
return target[proName]
},
// 在给源对象添加属性、修改属性的属性值时调用
// target 为源对象,propName 为要获取的属性名,value 为修改后的值
set(target, propName, value) {
target[propName] = value
},
// 在删除源对象的属性时调用
// target 为源对象,propName 为要获取的属性名
// 此函数需返回一个布尔值,表示是否删除成功
deleteProperty(target, propName) {
return delete target[propName]
}
})
p.name = "李四"
console.log(person.name) // 李四
此时,响应式的实现大致完成,但还不是最完美的解决方案。例如:
const p1 = Object.defineProperty(person, "name", {
get() {
return "李四"
}
})
// 此处出现异常
const p2 = Object.defineProperty(person, "name", {
get() {
return "王五"
}
})
console.log(person)
上述代码,在重复对 person
的 name
属性进行操作后,直接出现异常,导致单线程挂掉。如要解决以上问题,则需要用 try...catch
对异常进行捕获,非常的麻烦。
此时,我们可以使用 Reflect
处理这种问题。
Reflect
是 window
上的内置构造函数,目前 ECMA
组织正尽力于把 Object
内置构造函数上的有用的 api
移植到 Reflect
之上,如:set()
、get()
、deleteProperty()
…
与 Object
不同的是,Reflect
会返回一个布尔值,可以更灵活地捕捉到异常,判断操作是否成功,从而进一步对错误进行抛出。
结合以上所述,实现响应式可优化为:
const p = new Proxy(person, {
get(target, propName) {
return Reflect.get(target, propName)
},
set(target, propName, value) {
return Reflect.set(target, PropName, value)
},
deleteProperty(target, propName) {
return Reflect.deleteProperty(target, PropName)
}
})