Vue的核心功能点之一是响应式:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
简单的来说就是,页面的渲染效果会随着数据变化而变化,不用我们去手动操作DOM树进行数据变化后的渲染。为了实现这一目的,我们最简单的实现思路就是去监听每一个对象中的值,在我们获取或者更新这个值是插上一脚来操作DOM,此时,就可以通过函数去操作这个对象,以达到我们的目的。
vue2的响应式实现方法:defineProperty
Object.defineProperty() 是 JavaScript 中的一个方法,用于直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。它提供了精细的控制能力,可以定义属性的特性(特性包括可枚举、可配置、可写性等),并可以定义 getter 和 setter,在属性被访问或者设置时执行自定义的行为。
这里vue2的思路则为通过该方法来遍历对象,达到监听对象属性的目的,代码如下:
const obj = {a: 1,b: 2,c: {d: 3,e: 4,}
} as any//观察
const observer = (obj: any) => {for (const key in obj) {let temp = obj[key]if (typeof obj[key] === 'object') {observer(obj[key])} else {Object.defineProperty(obj, key, {get() {console.log('读取', key)return temp},set(val) {console.log('修改', key)temp = val}})}}
}observer(obj)obj.a;// 读取 a
obj.b = 3; // 修改 b
obj.c.d; // 读取 c.dobj.f = 5 // 新添属性 f 并修改 f
输出结果:
这里我们可以发现, 我们用递归的方式来去对每个值进行了监听,但是对应后面新增的对象属性就没有相对性的监听效果,如果想进行监听,就需要对这个属性进行单独的处理:
const obj = {a: 1,b: 2,c: {d: 3,e: 4,}
} as any//观察
const observer = (obj: any) => {for (const key in obj) {let temp = obj[key]if (typeof obj[key] === 'object') {observer(obj[key])} else {Object.defineProperty(obj, key, {get() {console.log('读取', key)return temp},set(val) {console.log('修改', key)temp = val}})}}
}const set = (obj: any, key: string, value: any) => {obj[key] = valuelet temp = obj[key]if (typeof value === 'object') {observer(value)} else {Object.defineProperty(obj, key, {get() {console.log('读取', key)return temp},set(val) {console.log('修改', key)temp = val}})}
}observer(obj)obj.a;// 读取 a
obj.b = 3; // 修改 b
obj.c.d; // 读取 c.dset(obj, 'f', {g: 5
}) // 修改 fobj.f.g; // 读取 f
输出结果:
vue3的响应式实现方法:Proxy
Proxy 是 ES6 中新增的一个功能,它允许你在访问一个对象之前定义自定义行为。通过 Proxy,你可以创建一个代理对象来包裹目标对象,并可以拦截并重定义该对象的各种操作,如属性的读取、赋值、删除等操作,甚至可以自定义对象的行为。
vue3使用的是Proxy来进行响应式操作的,这里就没必要将对象的所有属性进行遍历,而是使用哪个属性,则对哪个属性进行监听:
const obj ={a: 1,b: 2,c: {d: 3,e: 4,}
} as anyconst option = {get(target: any, key: any) {console.log('读取', key)//判断是否是对象if (typeof target[key] === 'object') {return new Proxy(target[key], option)}return target[key]},set(target: any, key: any, value: any) {console.log('设置', key, value)//判断是否是对象if (typeof value === 'object') {target[key] = new Proxy(value, option)return true}return true},
}const proxy = new Proxy(obj, option)proxy.a = 10; // 设置 a 10
proxy.a; // 读取 aproxy.c.f = 20; // 设置 c.f 20
proxy.c.f; // 读取 c.fproxy.c; // 读取 c
输出结果:
总结
vue2和vue3的响应式操作相比,vue2需要遍历对象,且对于新的属性无法直接进行监听,而vue3使用代理对象就不会出现这个问题。