这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
Observer-数据劫持
observer 用于给data中所有的数据添加getter和setter 方便我们在获取和设置data数据的时候,可以实现我们的逻辑
核心方法-- walk(data)
遍历data中的所有数据,都添加getter setter
核心方法-- defineReactive()
定义响应式的数据 (数据劫持)
defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log("你获取了值", value)
return value
},
set(newvalue) {
if (value === newvalue) {
return
}
console.log("你设置了值", newvalue)
value = newvalue
}
})
}
- 此时出现的问题是,对象的内部数据无法劫持设置 getter setter. 所以 如果data[key]是一个复杂的类型,就要进行递归的walk
- 但是新的问题出现,如果我改变数据,赋值一个非响应式对象进去,这时候新的对象数据里面没有响应式,所以在set里面继续执行walk函数
defineReactive(obj, key, value) {
let that = this
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
console.log("你获取了值", value)
return value
},
set(newvalue) {
if (value === newvalue) {
return
}
console.log("你设置了值", newvalue)
value = newvalue
that.walk(newvalue)
}
})
}
使用vue3数据劫持--vue3.0 使用Proxy代替了vue2.0版本中的Object.defineProperty(),首先利用compositionAPI中的 reactive() 函数返回一个Proxy对象,使得数据可监测。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
定义reactiveHandler处理对象 进行数据拦截
// 定义一个reactiveHandler处理对象
const reactiveHandler = {
// 获取属性值
get(target,prop){
const result = Reflect.get(target,prop);
console.log('拦截了读取数据',prop,result);
return result
},
// 修改属性值/添加属性
set(target,prop,value){
const result = Reflect.set(target,prop,value);
console.log('拦截了修改属性值或者是添加属性',prop,value);
return result
},
deleteProperty(target,prop){
const result = Reflect.deleteProperty(target,prop);
console.log('拦截了删除数据',prop);
return result
}
}
手写shallowReactive
// 定义一个shallowReactive函数,传入一个目标对象
function shallowReactive(target){
// 判断当前的目标对象是否是object类型(对象/数组)
if(target&&typeof target === 'object'){
return new Proxy(target,reactiveHandler)
}
// 如果传入的目标对象是基本类型的数据,则直接返回
return target
}
手写reactive
function reactive(target){
// 判断当前的目标对象是否是object类型(对象/数组)
if(target&&typeof target === 'object'){
// 对数组或者对象中所有的数据进行reactive的递归处理
// 判断当前的数据是否是数组
if(Array.isArray(target)){
// 数组的数据进行遍历操作
target.forEach((item,index)=>{
target[index] = reactive(item)
})
}else{
// 判断当前的数据是否是对象
// 对象的数据也要进行遍历的操作
Object.keys(target).forEach((key)=>{
target[key] = reactive(target[key])
})
}
return new Proxy(target,reactiveHandler)
}
// 如果传入的目标对象是基本类型的数据,则直接返回
return target
}
由此代替Object.defineProperty
数据劫持 具体区别:
1、Object.defineProperty
- 用于监听对象的数据变化
- 无法监听数组变化(下标,长度)
- 只能劫持对象的自身属性,动态添加的劫持不到 2、Proxy
- proxy返回的是一个新对象, 可以通过操作返回的新的对象达到目的 2)可以监听到数组变化,也可以监听到动态添加的数据