Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3-vue3.0 使用 proxy 重构的原因? #3

Open
ChenMingK opened this issue Aug 21, 2019 · 0 comments
Open

3-vue3.0 使用 proxy 重构的原因? #3

ChenMingK opened this issue Aug 21, 2019 · 0 comments

Comments

@ChenMingK
Copy link
Owner

参考链接:https://segmentfault.com/a/1190000015483195

首先罗列Object.defineProperty()的缺点:
1、Object.defineProperty()不会监测到数组引用不变的操作(比如push/pop等);
2、Object.defineProperty()只能监测到对象的属性的改变, 即如果有深度嵌套的对象则需要再次给之绑定Object.defineProperty();

关于Proxy的优点
1、可以劫持数组的改变;
2、defineProperty是对属性的劫持,Proxy是对对象的劫持;

为了加深理解,我们来举几个例子:
先来看看为什么说Object.defineProperty()不能监测到数组变化:

const data = {
  list: []
}

Object.keys(data).forEach(function (key) {
  let value = data[key]
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get () {
      return value
    },
    set (newValue) {
      console.log('set value')
      value = newValue
      return true
    }
  })
})

// 所谓对数组的监控,即能检测到数组元素的增加和删除
data.list.push(1) // nothing happen
console.log(data.list)
data.list = [2, 3] // set value
console.log(data.list)

当监控数组数据对象的时候,实质上就是监控数组的地址,地址不变也就不会被监测到,所以我们向 list 里 push 元素的时候并没有触发打印;当我们直接替换 list 对象的时候就触发了打印。所以这就是Object.defineProperty在数组监控方面的不足。
我们总是说 Vue 使用 hack 方法弥补了这个缺陷,那么看下其大致思路:

let arrayMethod = Object.create(Array.prototype);
    ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
        Object.defineProperty(arrayMethod, method, {
            enumerable: true,
            configurable: true,
            value: function () {
                let args = [...arguments]
                Array.prototype[method].apply(this, args);
                console.log(`operation: ${method}`)
                dep.notify();
            }
        })
    });
    
[Array Object].__proto__ = arrayMethod;

其核心思想就是覆写数组对象中的方法,在调用数组方法的同时能触发回调。同时这样做的好处是,仅仅是数据中的数组对象的原型被修改掉了,并不会影响到全局的数组对象。
最后用 proxy 来试下其是否能检测到数组元素变化:

const data = {
  list: [1, 2, 3, 4]
}

const proxyArr = new Proxy (data.list, {
  get: function (target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set: function (target, key, value, receiver) {
    console.log(target, key, value, receiver)
    return Reflect.set(target, key, value, receiver)
  }
})

proxyArr.push(2)
// [ 1, 2, 3, 4 ] '4' 2 [ 1, 2, 3, 4 ]
// [ 1, 2, 3, 4, 2 ] 'length' 5 [ 1, 2, 3, 4, 2 ]
proxyArr.shift()
/*
  [ 1, 2, 3, 4 ] '4' 2 [ 1, 2, 3, 4 ]
  [ 1, 2, 3, 4, 2 ] 'length' 5 [ 1, 2, 3, 4, 2 ]
  [ 1, 2, 3, 4, 2 ] '0' 2 [ 1, 2, 3, 4, 2 ]
  [ 2, 2, 3, 4, 2 ] '1' 3 [ 2, 2, 3, 4, 2 ]
  [ 2, 3, 3, 4, 2 ] '2' 4 [ 2, 3, 3, 4, 2 ]
  [ 2, 3, 4, 4, 2 ] '3' 2 [ 2, 3, 4, 4, 2 ]
  [ 2, 3, 4, 2, <1 empty item> ] 'length' 4 [ 2, 3, 4, 2, <1 empty item> ]
*/

可以看到确实可以监测到数组元素变化。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant