MVVM框架介绍--简易mvvm框架(3) | 青训营笔记

137 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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

image.png

  • 但是新的问题出现,如果我改变数据,赋值一个非响应式对象进去,这时候新的对象数据里面没有响应式,所以在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

  1. 用于监听对象的数据变化
  2. 无法监听数组变化(下标,长度)
  3. 只能劫持对象的自身属性,动态添加的劫持不到 2、Proxy
  4. proxy返回的是一个新对象, 可以通过操作返回的新的对象达到目的 2)可以监听到数组变化,也可以监听到动态添加的数据