functioninitProps(vm: Component,propsOptions: Object){const propsData = vm.$options.propsData ||{}const props = vm._props ={}// cache prop keys so that future props updates can iterate using Array// instead of dynamic object key enumeration.const keys = vm.$options._propKeys =[]const isRoot =!vm.$parent
// root instance props should be convertedif(!isRoot){toggleObserving(false)}for(const key in propsOptions){
keys.push(key)const value =validateProp(key, propsOptions, propsData, vm)/* istanbul ignore else */if(process.env.NODE_ENV!=='production'){...}else{defineReactive(props, key, value)}// static props are already proxied on the component's prototype// during Vue.extend(). We only need to proxy props defined at// instantiation here.if(!(key in vm)){proxy(vm,`_props`, key)}}toggleObserving(true)}
props 的初始化主要过程,就是遍历定义的 props 配置
通过 defineReactive 方法把每个 prop 对应的值变成响应式;
通过 proxy 方法把 vm._props.xxx 的访问代理到 vm.xxx 上 // 这样就能通过实例直接使用了
initData
functioninitData(vm: Component){let data = vm.$options.data
data = vm._data =typeof data ==='function'?getData(data, vm): data ||{}if(!isPlainObject(data)){
data ={}...}// proxy data on instanceconst keys = Object.keys(data)const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while(i--){const key = keys[i]...if(props &&hasOwn(props, key)){...}elseif(!isReserved(key)){proxy(vm,`_data`, key)}}// observe dataobserve(data,true/* asRootData */)}
data 的初始化主要过程做两件事,
一个是对定义 data 函数返回对象的遍历,
通过 proxy 把每一个值 vm._data.xxx 都代理到 vm.xxx 上;
另一个是调用 observe 方法观测整个 data 的变化,把 data 也变成响应式,
src/core/observer/index.js
exportfunctionobserve(value: any,asRootData:?boolean): Observer |void{if(!isObject(value)|| value instanceofVNode){return}letob: Observer |voidif(hasOwn(value,'__ob__')&& value.__ob__ instanceofObserver){
ob = value.__ob__
}elseif(
shouldObserve &&!isServerRendering()&&(Array.isArray(value)||isPlainObject(value))&&
Object.isExtensible(value)&&!value._isVue
){
ob =newObserver(value)}if(asRootData && ob){
ob.vmCount++}return ob
}
给非 VNode 的对象类型数据添加一个 Observer,
如果存在则直接返回这个Observer,否则在满足一定条件下去实例化一个 Observer 对象实例
Observer类
//作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新exportclassObserver{value: any;dep: Dep;vmCount: number;// number of vms that have this object as root $dataconstructor(value: any){this.value = value
this.dep =newDep()this.vmCount =0def(value,'__ob__',this)if(Array.isArray(value)){if(hasProto){protoAugment(value, arrayMethods)}else{copyAugment(value, arrayMethods, arrayKeys)}this.observeArray(value)}else{this.walk(value)}}walk(obj: Object){const keys = Object.keys(obj)for(let i =0; i < keys.length; i++){defineReactive(obj, keys[i])}}/**
* Observe a list of Array items.
*/observeArray(items: Array<any>){for(let i =0, l = items.length; i < l; i++){observe(items[i])}}}
构造函数逻辑很简单,首先实例化 Dep 对象,
接着通过执行 def 函数把自身实例添加到数据对象 value 的 __ob__ 属性上,
接下来会对 value 做判断,对于数组会调用 observeArray 方法,
否则对纯对象调用 walk 方法。可以看到 observeArray 是遍历数组再次调用 observe 方法,
而 walk 方法是遍历对象的 key 调用 defineReactive 方法
def 定义在 src/core/util/lang.js 中:
/**
* Define a property.
*/exportfunctiondef(obj: Object,key: string,val: any, enumerable?: boolean){
Object.defineProperty(obj, key,{value: val,enumerable:!!enumerable,writable:true,configurable:true})}
这就是为什么输出 data 上复杂类型的数据,会发现多了一个 __ob__ 的属性。