• proxy写监听方法,实现响应式


    var data = { price: 5, quantity: 2 };var data_without_proxy = data; // 保存源对象
    data = new Proxy(data_without_proxy, {
    // 重写数据以在中间创建一个代理
    get(obj, key) {
      console.log(obj+'取值')
    },
    set(obj, key, newVal) {
      console.log(obj+"设置值")
    }
    });

    data.price = 8 //设置

    data.price //取值

    【第1420期】JavaScript 响应式与 Proxy

    花生 前端早读课 今天

    前言

    前端的温度下降了吗?今日早读文章由@李金超推荐,汽车之家@花生翻译分享。

    @花生,就职于汽车之家用户产品中心团队,云云搬砖码农的中的一员。

    正文从这开始~~

    Vue与Proxy

    在之前的文章中,我们模拟了Vue的响应式引擎。使用Object.defineProperty()通过getters/setters实现属性的响应性。

    如果你一直有关注Vue的发展,就会发现2.x-next版本开始其响应式引擎将使用Proxy重写,这就与我们之前讲的不同了。

    1-1

    响应式引擎利用代理进行重写——标黄线的(译者注)。

    I wanted to ask Evan what exactly this might look like and the advantages we get from it(当成一句玩笑吧).

    这样做的好处

    Proxy允许我们创建一个对象的虚拟代理(替代对象),并为我们提供了在访问或修改原始对象时,可以进行拦截的处理方法(handler),如set()、get()和deleteProperty()等等。这样我们就可以避免很常见的这两种限制:

    • 添加新的响应性属性要使用Vue.$set(),删除现有的响应性属性要使用Vue.$delete()。

    • 数组的更新检测。

    之前的代码

    我们之前使用Object.defineProperty()来实现监听属性的访问和修改这两种操作,代码如下:

    let data = { price: 5, quantity: 2 }
    let target = null
    class Dep {
     constructor () {
       this.subscribers = []
     }
     depend () {
       if (target && !this.subscribers.includes(target)) {
         this.subscribers.push(target)
       }
     }
     notify () {
       this.subscribers.forEach(sub => sub())
     }
    }
    Object.keys(data).forEach(key => {
     let internalValue = data[key]
     const dep = new Dep()
     Object.defineProperty(data, key, {
       get() {
         dep.depend()
         return internalValue
       },
       set(newVal) {
         internalValue = newVal
         dep.notify()
       }
     })
    })
    function watcher(myFun) {
     target = myFun
     target()
     target = null
    }
    watcher(() => {
     data.total = data.price * data.quantity
    })
    console.log("total = " + data.total)
    data.price = 20
    console.log("total = " + data.total)
    data.quantity = 10
    console.log("total = " + data.total)

    使用Proxy克服限制

    我们可以使用以下方法在data对象上建立一个代理,而不是遍历每个属性来添加getter/setter。

    //  data 是我们准备要创建代理的源对象
    const observedData = new Proxy(data, {
     get() {
       //  访问源对象属性时调用
     },
     set() {
       //  修改源对象属性时调用
     },
     deleteProperty() {
       //  删除源对象属性时调用
     }
    });

    传递给Proxy构造函数的第二个参数可称为处理方法(handler),这是一个包含了陷阱(套路)函数的对象,可以使我们能够拦截发生在源对象上的操作。

    get()和set()就是两个陷阱,分别在调用dep.depend()和dep.notify()时触发。对于新添加的属性,也会调用set(),这样新添加的属性同样存在响应性。因此,我们不再需要使用Vue.$set()来添加新的响应性属性。同理,deleteProperty()同样适用。

    使用Proxy实现响应式

    尽管Proxy还没有被集成到Vue的响应引擎中,但是我们可以尝试一下,使用Proxy来实现之前文章中的例子。首先要更改的是Object.keys(data).forEach,我们现在将使用它为每个响应性属性创建一个新的依赖实例:

    let deps = new Map(); // 创建一个Map对象
    Object.keys(data).forEach(key => {
     // 为每个属性都设置一个依赖实例 并放入 deps 中
     deps.set(key, new Dep());
    });
    class Dep {
     constructor () {
       this.subscribers = []
     }
     depend () {
       if (target && !this.subscribers.includes(target)) {
         this.subscribers.push(target)
       }
     }
     notify () {
       this.subscribers.forEach(sub => sub())
     }
    }
    let data_without_proxy = data; // 保存源对象
    data = new Proxy(data_without_proxy, {
     // 重写数据以在中间创建一个代理
     get(obj, key) {
       deps.get(key).depend(); // <-- 依旧为存储target
       return obj[key]; // 返回原始数据
     },
     set(obj, key, newVal) {
       obj[key] = newVal; // 将原始数据设置为新值
       deps.get(key).notify(); // <-- 依旧为重新运行已存储的targets
       return true;
     }
    });

    注意:Dep class并不需要改动。单纯使用Proxy替换Object.defineProperty。

    如你所见,我们创建了一个变量data_without_proxy来作为源对象的副本,在覆盖源对象时来使用副本创建一个Proxy对象。第二个参数是包含了get()和set()这两个陷阱函数属性的handler对象。

    get(obj, key) => 是在访问属性时调用的函数。第一个参数obj为原始对象(data_without_proxy),第二个参数是被访问属性的key。这里面调用了与特定属性关联的特定方法(Dep class中的depend())。最后,使用return obj[key]返回与该key相关的值。

    set(obj, key, newVal) => 中前两个参数与get的相同,第三个参数是新的修改值,然后,我们将新值设置给obj[key] = newVal修改的属性上,并调用notify()方法。

    调整Total并测试

    我们需要对代码再做一个小小的修改,将total提取到它自己的变量中,因为它不需要存在响应性:

    let total = 0;
    watcher(() => {
     total = data.price * data.quantity;
    });
    console.log("total = " + total);
    data.price = 20;
    console.log("total = " + total);
    data.quantity = 10;
    console.log("total = " + total);

    现在,重新运行,我们会在控制台中看到如下:

    total = 10
    total = 40
    total = 200

    这是个不错的进展,当我们更新price和quantity时,total更新。

    添加新的响应性属性

    现在,我们应该可以在不事先声明属性的情况下,将属性添加到data中。这可能就是使用Proxy,而不是Object.defineProperty()的原因之一。我们可以添加如下代码,来尝试一下:

    deps.set("discount", new Dep());  // 为dep添加一个新属性
    data["discount"] = 5; // 为data添加同样的新属性
    let salePrice = 0;
    watcher(() => {  // 对其进行监听,其中包括我们新添加的属性
     salePrice = data.price - data.discount;
    });
    console.log("salePrice = " + salePrice);
    data.discount = 7.5;  // 此时就会调用我们的监听函数,达到响应式的目的
    console.log("salePrice = " + salePrice);

    运行后,我们可以看到如下输出:

    salePrice = 15
    salePrice = 12.5

    可以看到,当data.discount被修改时,salePrice也会更新。下面为完整的代码:

    let data = { price: 5, quantity: 2 };
    let target = null;
    class Dep {
     constructor() {
       this.subscribers = [];
     }
     depend() {
       if (target && !this.subscribers.includes(target)) {
         this.subscribers.push(target);
       }
     }
     notify() {
       this.subscribers.forEach(sub => sub());
     }
    }
    // 前边的代码都没变
    let deps = new Map(); // 创建一个Map对象
    Object.keys(data).forEach(key => {
     // 为每个属性都设置一个依赖实例 并放入 deps 中
     deps.set(key, new Dep());
    });
    let data_without_proxy = data; // 保存源对象
    data = new Proxy(data_without_proxy, {
     // 重写数据以在中间创建一个代理
     get(obj, key) {
       deps.get(key).depend(); // <-- 依旧为存储target
       return obj[key]; // 返回原始数据
     },
     set(obj, key, newVal) {
       obj[key] = newVal; // 将原始数据设置为新值
       deps.get(key).notify(); // <-- 依旧为重新运行已存储的targets
       return true;
     }
    });
    // 用来监听具有响应性属性的代码
    function watcher(myFunc) {
     target = myFunc;
     target();
     target = null;
    }
    let total = 0
    watcher(() => {
     total = data.price * data.quantity;
    });
    console.log("total = " + total);
    data.price = 20;
    console.log("total = " + total);
    data.quantity = 10;
    console.log("total = " + total);
    deps.set('discount', new Dep())
    data['discount'] = 5;
    let salePrice = 0;
    watcher(() => {
     salePrice = data.price - data.discount;
    });
    console.log("salePrice = " + salePrice);
    data.discount = 7.5
    console.log("salePrice = " + salePrice);

    总结

    我们大致知道了Vue在未来版本如何使用Proxy来实现响应式,也了解到:

    • 当前响应式引擎的局限性

    • Proxy是如何工作的

    • 如何使用Proxy来搭建一个响应式引擎

  • 相关阅读:
    Ad hoc access to OLE DB provider 'Microsoft.ACE.OLEDB.12.0' has been denied. You must access this provider through a linked server.
    阻塞问题:会话是sleeping的,但是open_tran 不是0
    windows Server DNS服务器配置
    内存缺页
    "ros::NodeHandle"的用法:全局vs.私有
    python 判断当前执行用户是否是 root 用户
    docker 安装及启动 postgresql 及navicat 连接
    Mac 在环境变量中隐藏密码或者密钥等信息
    磁盘空间不足导致虚拟机无法启动
    VirtuaBox 下安装 Centos8 无法上网
  • 原文地址:https://www.cnblogs.com/chaoyuehedy/p/9816789.html
Copyright © 2020-2023  润新知