• Vue3 为何使用 Proxy 实现数据监听


    博客地址:https://ainyi.com/93

    vue3 响应式数据放弃了 Object.defineProperty,而使用Proxy来代替它

    我们知道,在 vue2 中,实现数据监听是使用Object.defineProperty --> 实现方法可看:vue 数据双向绑定原理

    而这个方法有缺点,并且不能实现数组和对象的部分监听情况;具体也可以看我之前写的一篇博客:关于 Vue 不能 watch 数组 和 对象变化的解决方案

    最新的 Proxy,相比 vue2 的 Object.defineProperty,能达到速度加倍内存减半的成效。
    具体是怎么实现、以及对比旧的实现方法为啥能有速度加倍、内存减半的特性

    下面来聊聊

    Vue 初始化过程

    Vue 的初始化过程,分别有ObserverCompilerWatcher
    当我们 new Vue 的时候,会调用Observer,通过 Object.defineProperty 遍历 vue 对象的 data、computed 或者 props(如果是组件的话)的所有属性进行监听。同时通过Compiler解析模板指令,解析到属性后就 new 一个Watcher并绑定更新函数到 watcher 当中,Observer 和 Compiler 就通过属性来进行关联

    WechatIMG198.png

    如上,当 Observer 中的 setter 检测到属性值改变的时候,就调用属性对应的所有 watcher 调用更新函数,从而更新到属性对应的 dom

    Object.defineProperty

    来个简单的 Object.defineProperty 例子

    class Observer {
      constructor(data) {
        // 遍历参数 data 的属性,给添加到 this 上
        for(let key of Object.keys(data)) {
          if(typeof data[key] === 'object') {
            data[key] = new Observer(data[key]);
          }
          Object.defineProperty(this, key, {
            enumerable: true,
            configurable: true,
            get() {
              console.log('你访问了' + key)
              return data[key]
            },
            set(newVal) {
              console.log('你设置了' + key)
              console.log('新的' + key + '=' + newVal)
              if(newVal === data[key]) {
                return
              }
              data[key] = newVal
            }
          })
        }
      }
    }
    
    const obj = {
        name: 'app',
        age: '18',
        a: {
            b: 1,
            c: 2,
        },
    }
    const app = new Observer(obj);
    app.age = 20;
    console.log(app.age);
    app.newPropKey = '新属性';
    console.log(app.newPropKey);
    

    输出如下:

    你设置了age
    新的age=20
    你访问了age
    20
    新属性
    

    从上面可以知道:

    1. Object.defineProperty 需要遍历所有的属性,这就造成了如果 vue 对象的 data/computed/props 中的数据规模庞大,那么遍历起来就会慢很多
    2. 同理,如果 vue 对象的 data/computed/props 中的数据规模庞大,那么 Object.defineProperty 需要监听所有的属性的变化,那么占用内存就会很大

    Proxy

    再来看看 Proxy

    Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)

    可以理解为在对象之前设置一个”拦截“,当监听的对象被访问的时候,都必须经过这层拦截。可以在这拦截中对原对象处理,返回需要的数据格式
    也就是无论访问对象的什么属性,之前定义的或是新增的属性,都会走到拦截中进行处理。这就解决了之前所无法监听的问题

    官方例子:

    const p = new Proxy(target, handler)
    

    参数

    • target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
    • handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

    来个实际 Proxy 例子

    const obj = {
      name: 'krry',
      age: 24,
      others: {
        mobile: 'mi10',
        watch: 'mi4'
      }
    }
    const p = new Proxy(obj, {
      get(target, key, receiver) {
        console.log('查看的属性为:' + key);
        return Reflect.get(target, key, receiver);
      },
      set(target, key, value, receiver) {
        console.log('设置的属性为:' + key);
        console.log('新的属性:' + key, '值为:' + value);
        Reflect.set(target, key, value, receiver);
      }
    })
    p.age = 22
    console.log(p.age)
    console.log('--------')
    p.single = 'NO'
    console.log(p.single)
    console.log('--------')
    p.others.shoe = 'boost'
    console.log(p.others.shoe)
    

    输出如下:

    设置的属性为:age
    新的属性:age 值为:22
    查看的属性为:age
    22
    --------
    设置的属性为:single
    新的属性:single 值为:NO
    查看的属性为:single
    NO
    --------
    查看的属性为:others
    查看的属性为:others
    boost
    

    由上可知,新增或编辑属性,并不需要重新添加响应式处理,都能监听的到
    因为 Proxy 是对对象的操作,只要你访问对象,就会走到 Proxy 的逻辑中

    Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect不是一个函数对象,因此它是不可构造的

    区别

    Proxy 和 Object.defineProperty 的使用方法看似很相似,其实 Proxy 是在更高维度上去拦截属性的

    Object.defineProperty

    Vue2 中,对于给定的 data:如 { count: 1 },是需要根据具体的 key 也就是 count,去对 get 和 set 进行拦截,也就是:

    Object.defineProperty(data, 'count', {
      get() {},
      set() {},
    })
    

    必须预先知道要拦截的 key 是什么,这也就是为什么 Vue2 里对于对象上的新增属性无能为力,所以 Vue 初始化的过程中需要遍历 data 来挟持数据变化,造成速度变慢,内存变大的原因


    Proxy

    而 Vue3 所使用的 Proxy,则是这样拦截的:

    new Proxy(data, {
      get(key) { },
      set(key, value) { },
    })
    

    可以看到,proxy 不需要关心具体的 key,它去拦截的是 修改 data 上的任意 key读取 data 上的任意 key
    所以,不管是已有的 key 还是新增的 key,都会监听到
    但是 Proxy 更加强大的地方还在于 Proxy 除了 get 和 set,还可以拦截更多的操作符,具体可看 MDN

    兼容性

    Proxy 对 IE 不友好,vue3 在检测到使用 IE 的情况下(包括 IE11),会自动降级为 Object.defineProperty 的数据监听系统

    博客地址:https://ainyi.com/93

  • 相关阅读:
    多表关联查询_resultMap_集合对象
    mybatis多表关联查询之resultMap单个对象
    十二.filter
    十二.函数式编程
    十一.列表生成式
    十,迭代
    九.高级特性
    八.函数
    七.条件判断和循环
    六.使用list和tuple
  • 原文地址:https://www.cnblogs.com/ainyi/p/13447831.html
Copyright © 2020-2023  润新知