一、回答
reactive:使用Proxy对复合类型数据进行劫持,使用reactive方法包装后即可得到响应式的数据。
ref:vue3将普通类型的数据用ref函数包装成带有value属性的实例对象,该实例对象由vue3定义的名为RefImpl类生成。
既然要转换为带有value属性的对象,为什么不使用Proxy进行代理?原因是Proxy不止可以劫持数据获取和修改还能劫持其他功能。而普通数据类型仅需要监听获取和修改,所以出于性能的考虑并没有使用Proxy对其进行劫持,而是使用了Class中get和set关键字修饰自定义的value属性进行数据获取和变化的追踪。注意并不是vue2使用的Object.defineProperty。
ref函数既可以传递普通类型又能传递对象类型,它内部进行了参数类型的判断,如果是对象类型,它将由reactive函数进行包装,但是获取和更改时仍要使用 .value进行操作。
二、简单实现reactive和ref的示例
1.简单实现的reactive示例(非源码)。可以劫持到对象people的取值及变化
// 使用Proxy代理
function reactive(obj) {
let proxyPeople = new Proxy(obj, {
get: (target, key) => {
console.log(`获取了${key}属性`) // 获取了age属性
return target[key]
},
set: (terget, key, newVal) => {
console.log(`设置了${key}属性,新值为${newVal}`) // 设置了age属性,新值为18
terget[key] = newVal
}
})
return proxyPeople
}
// 要代理的原始数据
let people = reactive({
name: '小明'
})
// 添加属性
people.age = 18
console.log(people); // Proxy(Object) {name: '小明', age: 18}
console.log(people.age); // 18
2.简单实现的ref示例(非源码)。可以劫持到变量name的取值及变化
// 使用class关键字
function ref(variable) {
class RefImpl {
constructor(_value) {
this._value = _value
}
get value() {
console.log(`取值为${this._value}`) // 取值为小明 // 取值为小红
return this._value
}
set value(newVal) {
console.log(`新值为${newVal}`) // 新值为小红
this._value = newVal
}
}
return new RefImpl(variable)
}
let name = ref('小明')
console.log(name); // RefImpl {_value: '小明'}
console.log(name.value); // 小明
name.value = '小红'
console.log(name.value); // 小红