7. vue3.0 中为什么要使用 Proxy,它相比以前的实现方式有什么改进
7.1 原回答
-
Vue2.x通过给每个对象添加
getter setter
属性去改变对象,实现对数据的观测,Vue3.x通过Proxy代理目标对象,且一开始只代理最外层对象,嵌套对象
lazy by default
,性能会更好 - 支持数组索引修改,对象属性的增加,删除
7.2 查阅补充
(1)Proxy
-
Proxy 是一个对象,它包装了另一个对象,并允许你拦截对该对象的任何交互。
- Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
-
我们这样使用它:
const proxy = new Proxy(target, handler)
- target 是 目标对象
- proxy 是 代理对象
-
使用 Proxy 的一个难点是
this
绑定。-
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的
this
关键字会指向 Proxy 代理。const target = { m: function () { console.log(this === proxy); } }; const handler = {}; const proxy = new Proxy(target, handler); target.m() // false proxy.m() // true
上面代码中,一旦
proxy
代理
target
,
target.m()
内部的
this
就是指向
proxy
,而不是
target
。 -
我们希望任何方法都绑定到这个 Proxy,而不是目标对象,这样我们也可以拦截它们。值得庆幸的是,ES6 引入了另一个名为
Reflect
的新特性,它允许我们以最小的代价消除了这个问题:const dinner = { meal: 'tacos' } const handler = { get(target, property, receiver) { return Reflect.get(...arguments) } } const proxy = new Proxy(dinner, handler) console.log(proxy.meal) // tacos
-
-
Vue 响应性的步骤
-
当一个值被读取时进行追踪:proxy 的 get 处理函数中 track 函数记录了该 property 和当前副作用。
-
当某个值改变时进行检测:在 proxy 上调用 set 处理函数。
-
重新运行代码来读取原始值:trigger 函数查找哪些副作用依赖于该 property 并执行它们。
const dinner = { meal: 'tacos' } const handler = { get(target, property, receiver) { track(target, property)//1.一个名为 track 的处理器函数中跟踪一个 property 何时被读取.它将检查当前运行的是哪个副作用,并将其与 target 和 property 记录在一起。这就是 Vue 如何知道这个 property 是该副作用的依赖项。 return Reflect.get(...arguments) }, //2.在 property 值更改时重新运行这个副作用。为此,我们需要在代理上使用一个 set 处理函数 set(target, property, value, receiver) { trigger(target, property)//3.重新运行代码来读取原始值:trigger 函数查找哪些副作用依赖于该 property 并执行它们。 return Reflect.set(...arguments) } } const proxy = new Proxy(dinner, handler) console.log(proxy.meal) // tacos
-