先说一下vue数据初始化--initState
数据初始化
Vue 实例在建立的时候会运行一系列的初始化操作,而在这些初始化操作里面,和数据绑定关联最大的是 initState。
首先,来看一下他的代码:
function initState(vm) {
vm._watchers = [];
var opts = vm.$options;
if(opts.props) {
initProps(vm, opts.props); //初始化props
}
if(opts.methods) {
initMethods(vm, opts.methods); //初始化methods
}
if(opts.data) {
initData(vm); //初始化data
} else {
observe(vm._data = {}, true /* asRootData */ );
}
if(opts.computed) {
initComputed(vm, opts.computed); //初始化computed
}
if(opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch); //初始化watch
}
}
在这么多的数据的初始化中,props、methods和data是比较简单的(所以我就不详细介绍了☺),而computed 和 watch则相对较难,逻辑较复杂,所以我下面主要讲下computed 和 watch(以下代码部分为简化后的)。
initState里面主要是对vue实例中的 props, methods, data, computed 和 watch 数据进行初始化。
在初始化props的时候(initProps),会遍历props中的每个属性,然后进行类型验证,数据监测等(提供为props属性赋值就抛出警告的钩子函数)。
在初始化methods的时候(initMethods),主要是监测methods中的方法名是否合法。
在初始化data的时候(initData),会运行 observe 函数深度遍历数据中的每一个属性,进行数据劫持。
在初始化computed的时候(initComputed),会监测数据是否已经存在data或props上,如果存在则抛出警告,否则调用defineComputed函数,监听数据,为组件中的属性绑定getter及setter。如果computed中属性的值是一个函数,则默认为属性的getter函数。此外属性的值还可以是一个对象,他只有三个有效字段set、get和cache,分别表示属性的setter、getter和是否启用缓存,其中get是必须的,cache默认为true。
function initComputed(vm, computed) {
var watchers = vm._computedWatchers = Object.create(null);
for(var key in computed) {
var userDef = computed[key];
var getter = typeof userDef === 'function' ? userDef : userDef.get;
//创建一个计算属性 watcher
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
);
if(!(key in vm)) {
//如果定义的计算属性不在组件实例上,对属性进行数据劫持
//defineComputed 很重要,下面我们再说
defineComputed(vm, key, userDef);
} else {
//如果定义的计算属性在data和props有,抛出警告
}
}
}
在初始化watch的时候(initWatch),会调用vm.$watch函数为watch中的属性绑定setter回调(如果组件中没有该属性则不能成功监听,属性必须存在于props、data或computed中)。如果watch中属性的值是一个函数,则默认为属性的setter回调函数,如果属性的值是一个数组,则遍历数组中的内容,分别为属性绑定回调,此外属性的值还可以是一个对象,此时,对象中的handler字段代表setter回调函数,immediate代表是否立即先去执行里面的handler方法,deep代表是否深度监听。
vm.$watch函数会直接使用Watcher构建观察者对象。watch中属性的值作为watcher.cb存在,在观察者update的时候,在watcher.run函数中执行