前面我们讲了_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上,以$或_开头,如果是,则在开发环境中报警为当前实例添加一个方法;参考Vue3源码视频讲解:进入学习initDatainitData方法用于处理传入的data参数,添加监听函数initData(vm:Component){letdata=vm.$options.datadata=vm._data=数据类型==='函数'?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。`+`我们epropdefaultvalueinstead.`,vm)}elseif(!isReserved(key)){//实现代理,可以使用this.massage访问this._data.messageproxy(vm,`_data`,key)}}observe(data,true/*asRootData*/)}上面代码解读:第一步是获取传入的数据,判断数据是否为函数,如果是函数,则执行函数获取当前对象,否则直接读取当前对象;第二步,获取上一步数据的所有key,并赋值给key;第三步获取道具;道具中是否重复,如果重复,开发环境会做出报警判断。如果不是_或者$开头的key,会进行proxy处理,改进当前key对instance的访问方式:proxy,即可以使用vm.name访问vm._datas.name进行observe对当前数据对象进行处理,暂时先不关注observe,它做了什么,后面再说initComputedinitComputed用于处理传入的计算参数userDef=computed[key]constgetter=typeofuserDef==='function'?userDef:userDef.getif(!isSSR){watchers[key]=newWatcher(vm,getter||noop,noop,{lazy:true})}if(!(keyinvm)){defineComputed(vm,key,userDef)}}}initComputed方法解释:第一步是给实例添加_computedWatchers属性,声明并引用watchers;获取是否服务端渲染-isSSR;遍历计算;获取用户自定义内容——userDef根据用户自定义内容获取当前属性key的getter函数为当前key添加Watcher,暂时不关注Watcher我们来看defineComputed与userDef的实现:函数defineComputed(目标:任何,键:字符串,userDef:对象|函数){constshouldCache=!isServerRendering()if(typeofuserDef==='function'){sharedPropertyDefinition.get=shouldCache?createComputedGetter(key):createGetterInvoker(userDef)sharedPropertyDefinition.set=noop}else{sharedPropertyDefinition.get=?userDef.getshouldCache&&userDef.cache!==false?createComputedGetter(key):createGetterInvoker(userDef.get):noopsharedPropertyDefinition.set=userDef.set||noop}Object.defineProperty(target,key,sharedPropertyDefinition)}defineComputeddefineComputed方法解释:判断是否需要使用缓存,非服务器端渲染,使用缓存,即浏览器都是如此;分情况讨论:userDef为函数时,调用createComputedGetter函数生成get函数,set函数为空函数。当userDef不是函数时,get函数是createComputedGetter或createGetterInvoker生成的函数;调用Object.defineProperty为当前实例添加定义属性;createGetterInvoker让我们看一下createGetterInvoker:functioncreateGetterInvoker(fn){returnfunctioncomputedGetter(){returnfn.call(this,this)}}上面的代码直接返回一个函数,函数调用传入的fn函数。fn函数是从defineComputed传入的,值为userDef或userDef。getcreateComputedGetter让我们看看createComputedGetter:(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
