欢迎来到我的《从源码中学Vue》专题系列文章,更多精彩内容持续更新中,欢迎关注 :)
要说Vue的亮点有哪些,那么它的数据劫持绝对算是一大亮点。那么接下来我将通过源码去分析在Vue中的数据劫持究竟是怎么实现的
本章目标
- ES5中的Object.defineProperty基本用法
- Vue是如何做数据劫持的
Object.defineProperty
稍微对Vue数据劫持原理有了解的都知道,它就是通过Object.defineProperty这个ES5提供的Api来实现的。
那么在阅读源码之前,很有必要先去了解一下Object.defineProperty它的基本用法。
一般来说,我们去操作JS对象的时候,可以去获取对象的值,也可以去设置对象的值,像这样
这里有一个弊端就是,我们不管是在存数据或者是取数据的时候,仅仅是一个很单一的操作,这各过程中我们无法去做一些我们自定义的事情。
所以在ES5中为我们提供了Object.defineProperty来解决这个问题。
Object.defineProperty(obj,key,描述信息);这里的描述信息非常重要,我们来看最基础的用法。
可以看到,我们将对象通过Object.defineProperty来定义后,可以通过get和set方法去劫持Object对象属性发生变化。
这里面我们就可以做很多事情了。比如在数据改变后,去更新dom。
好了,接下来我们继续去看下Vue针对数据劫持的源码实现吧~
Vue是如何做数据劫持的
源码路径node_modulesvuesrccoreobserverindex.js
Observer类,在Vue中的作用就是观察类,专门用来监测Vue实例$data上的数据的变化。
在构造方法中,Vue优先判断了参数value的类型是否为数组,那么我们先就看下一般来说,Vue中在调用Observer的时候这个value到底传入的实参到底是什么?
我们进入到node_modulesvuesrccoreinstancestate.js中,找到一个叫initData的方法,这个方法处理Vue 实例上的data对象的,在该方法的最后面我们可以看到有一个observe方法。
这个方法传入的实参就是Vue实例上的$options.$data对象。
这个observe方法的定义的意思就是专门实例化Observer类,它的源码也在node_modulesvuesrccoreobserverindex.js中,
再回到我们的Observer类的定义当中,我们现在知道了,构造方法的每 个参数value,我们实际传入的是Vue实例下的data对象,也就是value是一个对象。
所以在构造方法中会走else里面的判断,即 walk方法被调用了。我们再去看看walk方法做了什么?
很简单,这个方法的作用就是将我们的data上每一条数据都定义了响应式。再来看defineReactive方法的具体实现。
大概看一下,它就是将我们的data数据中的每一个key值都通过Object.defineProperty来定义了。
首先我们来看这段源码
Object.getOwnPropertyDescriptor这个方法是用来获取我们通过 Object.defineProperty设置的描述。
来看一个示例
也就是说我们通过const property = Object.getOwnPropertyDescriptor(obj, key)获取到Object.defineProperty第三个参数的配置。
if (property && property.configurable === false) { return }
如果说不允许配置,那么后面的响应式也没意义,所以这里做了return 终止。
再往下看
childOb,从字面的上理解就是子观察对象。shallow意思为浅的,我们在walk方法中调用defineReactive方法的时候,没有传入shallow对应的参数,所以他返回false.
也就是说:childOb也接着会继续实例化Observer类,继续观察对象中的子对象。
应对的情况就是我们的data对象中的key对应的值也是一个对象。
这个时候,我们不仅要观察formObj对象,里面的对象我们需要继续观察。
难道Object.defineProperty不会主动去观察吗?我们来举个例子就知道了。
可以看到,我们重新在修改了obj.a为一个对象的时候,这个对象并没有设置get和set,
这也是Object.defineProperty的一个缺陷吧。
我们再去看看set方法中的实现
咦?这里怎么会又有一个childOb 呢??
我来解释一下:比如我们在data对象下定义一个formObj为1,我们通过vm.formObj = {name:123}了,这时候我们将formObj本来是一个普通的数字改成了一个对象值,所以这个时候我们需要继续去观察了。
总结:
- Object.defineProperty基本用法
- 我们通过Object.defineProperty内部通过get和set来劫持数据
这里是畅哥聊技术 《从源码中学Vue》系列文章,更多精彩内容持续更新中,敬请期待。
未完待续。。。