前面我们讲了_init函数的执行过程。简单回顾一下:初始化生命周期-initLifecycle初始化事件-initEvents初始化渲染函数-initRender调用钩子函数-beforeCreate初始化依赖注入-initInjections初始化状态信息-initState初始化依赖提供-initProvide调用钩子函数-created经历了以上共8步,执行init函数,开始挂载渲染。初始化状态信息本章我们主要讲解initState函数的处理过程。我们先看看init函数initState(vm:Component){vm._watchers=[]constopts=vm.$optionsif(opts.props){initProps(vm,opts.props)}if(opts.methods){initMethods(vm,opts.methods)}if(opts.data){initData(vm)}else{observe(vm._data={},true/*asRootData*/)}if(opts.computed){initComputed(vm,opts.computed)}if(opts.watch&&opts.watch!==nativeWatch){initWatch(vm,opts.watch)}}看上面的代码,首先声明一个空的_watchers数组;然后依次判断传入的选项是否包含一系列参数;依次执行initProps、initMethods、initData、initComputed、initWatch。initPropsinitProps函数主要是对传入的props对象进行处理,但是这个props对象是上一篇中提到的normalizeProps函数处理后的对象,并不是传入的原始对象。我们看一下initProps的代码:functioninitProps(vm:Component,propsOptions:Object){constpropsData=vm.$options.propsData||{}constprops=vm._props={}constkeys=vm.$options._propKeys=[]constisRoot=!vm.$parentif(!isRoot){toggleObserving(false)}for(constkeyinpropsOptions){keys.push(key)constvalue=validateProp(key,propsOptions,propsData,vm)defineReactive(props,key,value)if(!(keyinvm)){proxy(vm,`_props`,key)}}toggleObserving复制代码(true)}上面代码解读:第一步获取propsData;第二步给当前实例添加_props属性,并添加一个props引用,指向_props属性;第三步为当前实例添加_propKeys属性,一个新的keys引用指向_propKeys属性;第四步judges指示是否需要监听;遍历normalizeProps函数处理过的对象propsOptions;存储key验证props格式为当前key定义响应属性:defineReactive改进了当前key对实例的访问方式:proxy,即vm.name访问vm._props.name函数proxy(target:Object,sourceKey:string,key:string){sharedPropertyDefinition.get=functionproxyGetter(){返回这个[sourceKey][key]}sharedPropertyDefinition.set=functionproxySetter(val){this[sourceKey][key]=val}Object.defineProperty(target,key,sharedPropertyDefinition)}打开监听initMethodsinitMethods方法是用来处理传入的方法参数,把方法绑定到当前实例上面functioninitMethods(vm:Component,methods:Object){constprops=vm.$options.propsfor(constkeyinmethods){if(process.env.NODE_ENV!=='production'){if(typeofmethods[key]!=='function'){warn(`方法“${key}”的类型为“${typeofmethods[key]}"在组件定义中。`+`你是否正确引用了函数?`,vm)}if(props&&hasOwn(props,key)){warn(`Method"${key}"hasalreadybeendefinedasaprop.`,vm)}if((keyinvm)&&isReserved(key)){warn(`方法“${key}”与现有的Vue实例方法冲突。`+`避免定义以_或开头的组件方法$.`)}}vm[key]=typeofmethods[key]!=='function'?noop:bind(methods[key],vm)}}表面代代码解读:第一步是获取道具;第二步是遍历方法;判断当前方法是否为函数,如果不是函数,则在开发环境发出告警,判断props是否已经有当前方法的key,如果有则在开发环境发出告警environment判断当前方法是否已经在vm上,以$或_开头,如果是,则在开发环境中报警为当前实例添加一个方法;initDatainitData方法用于处理传入的数据参数,并添加监控函数initData(vm:Component){letdata=vm.$options.datadata=vm._data=typeofdata==='function'?getData(数据,虚拟机):数据||{}constkeys=Object.keys(data)constprops=vm.$options.propsconstmethods=vm.$options.methodsleti=keys.lengthwhile(i--){constkey=keys[i]if(process.env.NODE_ENV!=='production'){if(methods&&hasOwn(methods,key)){warn(`方法“${key}”已被定义为数据属性。`,vm)}}if(props&&hasOwn(props,key)){process.env.NODE_ENV!=='production'&&warn(`数据属性“${key}”已经声明为prop。`+`使用propdefaultVAlueinstead.`,vm)}elseif(!isReserved(key)){//实现代理,可以使用this.massage访问this._data.messageproxy(vm,`_data`,key)}}observe(data,true/*asRootData*/)}上面代码解读:第一步是获取传入的数据,判断数据是否为函数,如果是函数,则执行函数获取当前对象,否则直接读取当前对象;第二步,获取上述第一步,将数据的所有key赋值给keys;第三步,获取道具;第四步,获取方法;第五步,循环密钥;判断他们在methods上是否重复,如果重复,开发环境会报警判断是否和props一致重复,重复,开发环境会做出报警判断。如果key不以_或$开头,则会进行proxy处理,改进当前key对实例的访问方式:proxy,即可以通过vm.name访问vm._datas.name对当前data对象正在进行observe处理,暂时先不关注observe,后面会讲到observe是干什么的详细答案参考Vue面试题。initComputedinitComputed用于处理传入的计算参数=computed[key]constgetter=typeofuserDef==='函数'?userDef:userDef.getif(!isSSR){watchers[key]=newWatcher(vm,getter||noop,noop,{lazy:true})}if(!(keyinvm)){defineComputed(vm,key,userDef)}}}initComputed方法解释:第一步是给实例添加_computedWatchers属性,声明引用watchers;get是否是服务端渲染-isSSR;遍历计算;get自定义内容-userDef根据自定义内容获取当前属性key的getter函数为当前key添加Watcher,暂时不关注Watcher,后面会讲到调用defineComputed,参数是当前实例,当前属性key和userDef我们来看defineComputed的实现:functiondefineComputed(target:any,key:string,userDef:Object|Ffunction){constshouldCache=!isServerRendering()if(typeofuserDef==='function'){sharedPropertyDefinition.get=shouldCache?createComputedGetter(key):createGetterInvoker(userDef)sharedPropertyDefinition.set=noop}else{sharedPropertyDefinition.get=.get?shouldCache&&userDef.cache!==false?createComputedGetter(key):createGetterInvoker(userDef.get):noopsharedPropertyDefinition.set=userDef.set||noop}Object.defineProperty(target,key,sharedPropertyDefinition)}defineComputeddefineComputedmethod解释:判断是否使用缓存,非服务端渲染,使用缓存,即浏览器都为真;按情况讨论:当userDef为函数时,调用createComputedGetter函数生成get函数,set函数为空函数userDef不是函数,get函数为createComputedGetter或createGetterInvoker生成的函数;调用Object.defineProperty为当前实例添加定义属性;createGetterInvoker让我们看一下createGetterInvoker:functioncreateGetterInvoker(fn){returnfunctioncomputedGetter(){returnfn.call(this,this)}}上面代码直接返回一个函数,函数内部调用传入的fn函数,fn函数是从defineComputed传入的,值为userDef或者用户定义getcreateComputedGetter让我们来看看createComputedGetter:}if(Dep.target){watcher.depend()}returnwatcher.value}}}上面代码返回一个computedGetter函数,函数内部分析:获取到initComputed函数中声明的_computedWatchers,watcher必须有一个值,这里dirty属性的值也相当于lazy属性,因为创建watcher的时候传入了true,所以这里也是true;执行watcher.evaluate,该方法会获取当前watcher的值,并将dirty属性改为false;判断Dep.target,然后调用watcher的集合依赖;返回watcher.value;initWatchinitWatch用于处理传入的监视参数。functioninitWatch(vm:Component,watch:Object){for(constkeyinwatch){consthandler=watch[key]if(Array.isArray(handler)){for(leti=0;i
