继续随着核心类的初始化展开探索其他的模块,这一篇来研究一下Vue的状态初始化。这里的状态初始化指的就是在创建实例的时候,在配置对象里定义的属性、数据变量、方法等是如何进行初始处理的。由于随后的数据更新变动都交给观察系统来负责,所以在事先弄明白了数据绑定的原理之后,就只需要将目光集中在这一部分。
来仔细看看在核心类中首先执行的关于 state 部分的源码:
initState
// 定义并导出initState函数,接收参数vm export function initState (vm: Component) { // 初始化实例的私有属性_watchers // 这就是在观察系统里会使用到的存储所有显式监视器的对象 vm._watchers = [] // 获取实例的配置对象 const opts = vm.$options // 如果定义了props,则初始化props if (opts.props) initProps(vm, opts.props) // 如果定义了methods,则初始化methods if (opts.methods) initMethods(vm, opts.methods) // 如果定义了data,则初始化data if (opts.data) { initData(vm) } else { // 否则初始化实例的私有属性_data为空对象,并开启观察 observe(vm._data = {}, true /* asRootData */) } // 如果定义了computed,则初始化计算属性 if (opts.computed) initComputed(vm, opts.computed) // 如果定义了watch并且不是nativeWatch,则初始化watch // nativeWatch是火狐浏览器下定义的对象的原型方法 if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
这段代码非常直白,主要用来执行配置对象里定义的了状态的初始化。这里分别有 props、data、methods、computed、watch 五个配置对象,分别有各自的初始化方法。在仔细研究它们的具体实现之前,先来看一段将在各个初始化函数里用到的辅助函数。
// 定义共享属性定义描述符对象sharedPropertyDefinition // 描述符对象的枚举和可配置属性都设置为true // get、set方法设置为空函数 const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } // 定义并导出proxy函数,该函数用来为在目标对象上定义并代理属性 // 接收目标对象target,路径键名sourceKey,属性键名三个参数 export function proxy (target: Object, sourceKey: string, key: string) { // 设置属性描述符对象的get方法 sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } // 设置属性描述性对象的set犯法 sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } // 在目标对象上定义属性 Object.defineProperty(target, key, sharedPropertyDefinition) }
proxy 函数的定义非常重要,在下面要探究的各个初始化函数中它,它会将我们在配置对象中设置的属性全部定义到实例对象中,但是我们对这些属性的操作是通过各部分相应的代理属性上来执行的。get 和 set 方法的实现非常明白的表示出这一过程,然后再将属性定义到实例中。由这个函数作为基础,继续来看看其他五个状态的初始化函数的内容。