最近钻研vue3源码, 晓得应用了Proxy和Reflect, 然而不理解它们之间的关系

这篇文章次要是让大家理解vue3为什么应用Proxy和Reflect以及响应式的局部原理

为什么应用Proxy(Proxy和Object.defineproperty)

Object.defineproperty实现对象监听

先解析一下vue2中应用的Object.defineproperty

let obj = {    a: 10}Object.keys(obj).forEach(key => {    let value = obj[key]    Object.defineProperty(obj, key, {        set(newValue) {            console.log(`监听${key}扭转: ${newValue}`);            value = newValue        },        get() {            console.log(`获取${key}对应的值: ${value}`);            return value        }    })})obj.a = 100 // 监听a扭转: 100obj.b = 10 // 不会被监听到

通过下面的例子咱们能够看到obj新增加的属性b, 并不会被监听到
vue2中应用中咱们也会遇到这样的问题

# template<p @click="adda(obj)">{{ obj.a }}</p><p @click="addb(obj)">{{ obj.b }}</p># srcriptdata () {    return {        obj:{            a:1        }    }},mounted () {    this.obj.b = 1;},methods: {    addb(item){        item.b += 1;        console.log(this.obj.b)    },    adda(item){        item.a += 1;    }}

咱们发现点击obj.a是响应式, 页面也会更新
而新增的obj.b点击则不会

因为vue2应用的Object.defineproperty无奈监听到新增的对象属性

针对这个问题vue2提供了$set办法来解决

mounted () {    this.$set(this.obj, "b", 1)}
Proxy实现对象监听
let obj = {    a: 10}const handler = {    get(target, prop) {        console.log(`获取${prop}对应的值: ${target[prop]}`);        return target[prop];    },    set(target, prop, val) {        target[prop] = val;        console.log(`监听${prop}扭转: ${val}`);        return true    }}let obj2 = new Proxy(obj, handler)obj2.b = 100 // 监听b扭转: 100

咱们能够看到通过Proxy实例能够对新增加的属性进行监听

当然Proxy还能够做许多的其余性能, 这里就不多介绍了

我查看Vue3的源码的时候始终对Proxy中应用的Reflect感到不解,为什么要应用Reflect.get和Reflect.set, 我查问了一些文章, 大略了一下思路

Reflect

我将通过一些问题, 来指明Reflect中Proxy中的用途

咱们有一个user带有_name属性的对象和一个吸气剂。

这是围绕它的代理:

let user = {  _name: "Guest",  get name() {    return this._name;  }};let userProxy = new Proxy(user, {  get(target, prop, receiver) {    return target[prop];  }});console.log(userProxy.name); // Guest

对于咱们的示例而言,这就足够了。

所有仿佛都还好。然而,让咱们将示例变得更加简单。

继承另一个对象后admin从user,咱们能够察看到不正确的行为:

let user = {  _name: "Guest",  get name() {    return this._name;  }};let userProxy = new Proxy(user, {  get(target, prop, receiver) {    console.log(target) // user对象{_name: "Guest"}    return target[prop];  }});let admin = {  __proto__: userProxy,  _name: "Admin"};console.log(admin.name); // Guest

浏览admin.name应该返回"Admin",而不是"Guest"!

怎么了?兴许咱们在继承方面做错了什么?

问题实际上出在代理所在的行中:

  1. 当咱们浏览时admin.name,因为admin对象没有本人的属性,搜寻将转到其原型。
  2. 原型是userProxy
  3. name从代理读取属性时,其get将触发并从原始对象中返回该属性,它在上下文中运行其代码this=target。因而,后果this._name来自原始对象target,即:from user

而这个时候就是Reflect.get就派上用场了

如果咱们应用它,所有都会失常运行。

let user = {  _name: "Guest",  get name() {    return this._name;  }};let userProxy = new Proxy(user, {  get(target, prop, receiver) { // receiver = admin    return Reflect.get(target, prop, receiver);  }});let admin = {  __proto__: userProxy,  _name: "Admin"};console.log(admin.name); // Admin

Reflect.get中receiver参数,保留了对正确援用this(即admin)的援用,该援用将Reflect.get中正确的对象应用传递给get。

相干文章

https://javascript.info/proxy