You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The static Reflect.get() method works like getting a property from an object (target[propertyKey]) as a function.
按照说明,似乎是没有区别的,不过关键是第三个参数 Reciever: receiver Optional:
The value of this provided for the call to target if a getter is encountered. When used with Proxy, it can be an object that inherits from target.
这个参数可以指定取值时的 this? 大家肯定会奇怪取值的时候哪来的 this 呢? 我们把前面的例子改一下就知道了:
Vue3响应式系统设计理念
先说点题外话,
Vue3
正式发布之后,我面试的时候如果发现对方简历写有“精通Vue”之类的话,一般都会问一问Vue3
相对Vue2
有哪些变化,很多人只能答上来一点就是响应式从defineProperty
变成了proxy
。其实Vue3
是一次比较大的重构,变化的地方非常多,举几个重要的点:上面的题外话有助于理解接下来的内容。回到本文议题,在
Vue3
中响应式模块其实有两个层次的变化:definePropery
变成了proxy
,规避了一些老版本难以解决的bugreactivity
变成了一个功能完善的有明确API定义的独立模块,而不是耦合在Vue源码中,这就意味着我们可以在任何项目中引入reactivity
不需要依赖Vue
框架既然是一个独立的模块,那么它就会有自己完整的设立思路。响应式系统的设计思路是什么?总结为一句话就是:在目标发生变化时,执行对应的函数。顺着这个思路,我们就可以定义出响应式系统的几个要素:
target
和effectFn
target
和effectFn
之间的关联,当target
变化之后自动执行effectFn
怎么实现上面两个要素呢?
Vue3
中是这么实现的:reactive
函数用来声明需要被监听的目标,提供effect
函数用来声明需要执行的函数target
和effectFn
的关联关系,主要是提供track
和trigger
两个函数分别收集和触发依赖。42行代码实现
reactive
函数将一个target
变成响应式的,原理是通过proxy
进行了代理,这里为了说明原理,我们只实现对Object
类型的代理,代码如下:用法如下所示:
reactive
的基本原理是在get
的时候收集依赖,在set
的时候触发依赖。这种依赖收集方式非常巧妙,使得我们代码中不必额外写依赖声明,只要读取了值就会自动收集依赖。track
和trigger
函数分别是用来记录依赖和触发依赖的,配合 effect记录当前函数,实现如下:用法如下:
上面 22行实现代码,有三个要点(面试考点)需要注意。
第一个要点是
effect
实现。effect
作用就是记录并执行传入的fn
,这里为什么可以用一个全局变量来记录呢? 因为JS的代码执行是单线程的(不考虑worker),effect
函数不可能并行执行,因此这样记录没有问题。而且这不是为了实现简单随便写的,官方实现也是一个全局变量。effect
中执行fn()
时,会触发对p
的读取操作,此时就会调用track
函数记录依赖。第二个要点是三个数据结构
WeakMap
,Map
和Set
。targetMap
为什么不用Map
或者Object
呢?主要是两个原因:WeakMap
可以用任意的JS类型作为key
,这里我们需要用target
对象作为key
WeakMap
对key
的应用是弱引用,不会影响垃圾回收。那
dep
为什么可以用Map
呢? 因为dep
整体会被作为垃圾回收,通过key
持有引用不会影响垃圾回收,而且key
一定是一个字符串。为什么
funcs
用Set
而不用数组呢?因为Set
是自动去重的。第三个要点 是
targetMap
的结构,依赖信息是如何记录的。结构是这样的:targetMap[target][key] = new Set(fn1, fn2, fn3);
为什么要用
Reflect
还有一个非常需要注意的点,是对
Reflect
的使用。大家考虑下这两行代码有什么区别?假设我们把
Reflect.get
换成target[key]
会有什么问题吗? 要回答这个问题,先看看MDN上的定义:当取值
name
的时候,这不就有this
了吗?此时如果我们通过target[key]
取值,相当于通过raw.name
进行了取值,那么其中的this
就指向了raw
而不是person
。这样就会有问题了,因为只有person.firstName
才会进入getter
收集依赖,raw.firstName
并不会触发依赖收集。结论就是:
Reflect
是为了解决this
指向问题,如果用target[key]
会导致this
指向原始值而无法收集到依赖。和官方实现有什么区别?
前文的42行玩具实现其实已经揭示了核心逻辑,当显然不能和官方2000行代码实现相媲美。那么官方的这么多代码额外做了哪些工作呢?总结一下:
2.
effect
执行的时候支持lazy
模式,支持自定义调度器Ref
,Computed
等API上文中的完整的42行代码参见这里:https://github.com/lihongxun945/42lines-vue3-reactivity/blob/master/reactivity.js
The text was updated successfully, but these errors were encountered: