本节的示例如下
当new vue的时候将执行init方法,在该方法中将调用initState
这里有两个地方比较重要,一个是initProps,一个是initData
我们本节没有props数据,故着重看data是如何被set为响应式的
我们示例的data是一个函数,故调用getData方法,传入data和vm实例
该方法将修正this指向,使其指向vm实例,因此data拿到的就是一个this指向vm的对象,因此我们可以在组件内通过this._data拿到data中的值,因此最终拿到的data是一个对象,如果不是,则报警告,即isPlainObject为false
接着拿到data、props和methods的键名去遍历比对,如果发现data中的键在props或者methods中同名,则报警告,同时针对键名也做了限制,比如不能以_或$开头等
接着调用observe对data进行观察(观察者模式),并将data传入
vue要求value必须是一个对象,且不能是vnode
显然,作为第一次,value上是没有__ob__属性的,因此进入else逻辑
shouldObserve是在全局定义的值,默认为true,即应该对该对象进行观测,同时提供了toggleObserving方法去控制跳过某些不需要观测的值
isServerRendering用于检测当前的代码环境,我们这里是在浏览器环境故进入else,返回false
我们的value是对象,故isPlainObject为true
isExtensible是原生Object提供的检测目标对象是否可扩展,需要手动调用preventExtensions置为false否则默认是可扩展的,故我们这里是true
_isVue在vue的时候为true,因此这里为false
实例化Observer
向Observer的value保存一份data
向Observer的dep保存一份Dep实例,这里的Dep实际上充当的就是"发布者"
调用def,def实际是对Object.defineProperty的封装
在这里向value添加了私有属性"__ob__",该值是不可枚举的,且它的值引用的是Observer实例
我们这里的value是对象,故走else逻辑,调用walk方法
在这里遍历value的每一个key并调用defineReactive将value和键值传入,由于我们将__ob__设置为不可枚举,故实际上只会执行一次,即key="meta"
首先,再次实例化dep
通过Object.getOwnPropertyDescriptor获取对象的属性描述符,默认情况下符合if判断
默认是没有get和set的,故getter=undefined,setter=undefined,调用该函数时传递的实参只有两个,故符合if判断。val=obj.msg,即我们在data中定义的{msg:"腊八节快乐呀!"}
接着继续对val尝试进行观测,由于是一个对象,因此会再次执行到当前位置,下一次的值为msg是一个基本数据类型 String,故向下调用Object.defineProperty,定义get和set。其中get将在每次访问时进行依赖收集,set则主要做派发更新
当msg被通过Object.defineProperty定义过get和set后,将会返回到上一次值为{msg:"腊八节快乐呀!"},并对该对象设置set和get,接着继续返回meta.......,也就是说,vue会进行深度遍历,对每一个值都添加set和get同时每一个值都添加了私有的__ob__。这样当访问不管是哪一个值时都架设了一层拦截器