Vue3 如何实现响应式 - Proxy(笔记自用)

目录

1. 回顾 Vue2 的 Object.defineProperty

2. Proxy 实现响应式

2.1 基本使用

2.2 实现响应式 

3. 总结


1. 回顾 Vue2 的 Object.defineProperty

缺点:

  • 深度监听需要一次性递归,影响性能
  • 无法监听新增属性和删除属性(需要:Vue.set Vue.delete)
  • 无法原生监听数组,需要特殊处理

2. Proxy 实现响应式

Proxy ES6 新增

  • 监听一个对象的相关操作,先创建一个这个对象的代理对象(Proxy 对象)
  • 之后对修改对象的所有操作,都通过代理对象完成
  • 代理对象可以监听到我们想要对原对象进行哪些操作

Reflect ES6 新增 API 

  • 它是一个对象,字面意思是反射
  • 它提供了很多操作 JavaScript 对象的方法,有点像 Object 中操作对象的方法
  • 比如 Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()
  • 比如 Reflect.defineProperty(target, propertyKey, attributes)类似 Object.defineProperty()
  • 和 Proxy 能力一一对应
  • 规范化,标准化,函数式(之前:‘a’ in obj  ;现在:Reflect.has(obj, 'a') )
  • 替代掉 Object 上的工具函数(Object.getOwnPropertyNames(obj) =》Reflect.ownKeys(obj))使 Object 专注做一个数据结构,为 JS 服务,避免大而全,不利于编程

2.1 基本使用

// const data = {
//     name: 'zhangsan',
//     age: 20,
// }
const data = ['a', 'b', 'c']

const proxyData = new Proxy(data, {
    get(target, key, receiver) {
        // 只处理本身(非原型的)属性
        const ownKeys = Reflect.ownKeys(target)
        if (ownKeys.includes(key)) {
            console.log('get', key) // 监听
        }

        const result = Reflect.get(target, key, receiver)
        return result // 返回结果
    },
    set(target, key, val, receiver) {
        // 重复的数据,不处理
        if (val === target[key]) {
            return true
        }

        const result = Reflect.set(target, key, val, receiver)
        console.log('set', key, val)
        // console.log('result', result) // true
        return result // 是否设置成功
    },
    deleteProperty(target, key) {
        const result = Reflect.deleteProperty(target, key)
        console.log('delete property', key)
        // console.log('result', result) // true
        return result // 是否删除成功
    }
})

2.2 实现响应式 

  • Proxy 深度监听性能更好
    Vue2 的 Object.defineProperty 一次性递归监听
    Vue3 的 Proxy 在 get 时才递归监听
  • Proxy 可监听 新增 / 删除属性
  • 可原生监听数组变化
// 创建响应式
function reactive(target = {}) {
    if (typeof target !== 'object' || target == null) {
        // 不是对象或数组,则返回
        return target
    }

    // 代理配置
    const proxyConf = {
        get(target, key, receiver) {
            // 只处理本身(非原型的)属性
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('get', key) // 监听
            }
    
            const result = Reflect.get(target, key, receiver)
        
            // 深度监听
            // 性能如何提升的?get 时才递归监听
            return reactive(result)
        },
        set(target, key, val, receiver) {
            // 重复的数据,不处理
            if (val === target[key]) {
                return true
            }
    
            const ownKeys = Reflect.ownKeys(target)
            if (ownKeys.includes(key)) {
                console.log('已有的 key', key)
            } else {
                console.log('新增的 key', key)
            }

            const result = Reflect.set(target, key, val, receiver)
            console.log('set', key, val)
            // console.log('result', result) // true
            return result // 是否设置成功
        },
        deleteProperty(target, key) {
            const result = Reflect.deleteProperty(target, key)
            console.log('delete property', key)
            // console.log('result', result) // true
            return result // 是否删除成功
        }
    }

    // 生成代理对象
    const observed = new Proxy(target, proxyConf)
    return observed
}

// 测试数据
const data = {
    name: 'zhangsan',
    age: 20,
    info: {
        city: 'beijing',
        a: {
            b: {
                c: {
                    d: {
                        e: 100
                    }
                }
            }
        }
    }
}

const proxyData = reactive(data)

3. 总结

  • Proxy 能规避 Object.defineProperty 的问题
  •  Proxy 无法兼容所有浏览器,无法 polyfill

猜你喜欢

转载自blog.csdn.net/weixin_39763711/article/details/126628583