当前位置: 首页 > Web前端 > vue.js

newVue发生了什么

时间:2023-03-31 23:20:34 vue.js

从入口代码开始,我们先来分析一下newVue背后发生了什么。我们都知道Javascript语言中的new关键字表示实例化是一个对象,而Vue其实是一个类。类在Javascript中使用Function实现。我们看一下源码,在src/core/instance/index.js中。functionVue(options){if(process.env.NODE_ENV!=='production'&&!(thisinstanceofVue)){warn('Vue是一个构造函数,应该使用`new`关键字调用')}//在实例化vue对象时//调用init方法配置optionsthis._init(options)}可以看到Vue只能通过new关键字进行初始化,然后会调用this._init方法,在src中/core定义在/instance/init.jsVue.prototype._init=function(options?:Object){constvm:Component=this//auidvm._uid=uid++letstartTag,endTag/*istanbulignoreif*/if(process.env.NODE_ENV!=='production'&&config.performance&&mark){startTag=`vue-perf-start:${vm._uid}`endTag=`vue-perf-end:${vm._uid}`mark(startTag)}//避免被观察到的标志vm._isVue=true//合并选项if(options&&options._isComponent){//优化内部组件实例化//因为动态选项合并非常慢,并且没有任何//内部组件t选项需求specialtreatment.initInternalComponent(vm,options)}else{vm.$options=mergeOptions(resolveConstructorOptions(vm.constructor),options||{},vm)}/*istanbul忽略其他*/if(process.env.NODE_ENV!=='production'){initProxy(vm)}else{vm._renderProxy=vm}//暴露真实的自我vm._self=vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm,'beforeCreate')initInjections(vm)//在数据/道具之前解决注入initState(vm)initProvide(vm)//resolveprovideafterdata/propscallHook(vm,'created')/*istanbulignoreif*/if(process.env.NODE_ENV!=='production'&&config.performance&&mark){vm._name=formatComponentName(vm,false)mark(endTag)measure(`vue${vm._name}init`,startTag,endTag)}if(vm.$options.el){vm.$mount(vm.$options.el)}}Vue初始化主要做了几个东西,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化数据,props,computed,watcher等。vue初始化我们其实可以看到代码层次非常清晰。//关于initState()方法。仔细研究下一个问题:我们都知道,如果要访问vue中Data属性的值,比如data(){return{b:1}}//可以直接通过this.b访问这个值,但为什么会这样呢?我们来分析函数initData()exportfunctioninitState(vm:Component){vm._watchers=[]constopts=vm.$optionsif(opts.props)initProps(vm,opts.props)if(opts.methods)initMethods(vm,opts.methods)if(opts.data){//如果opts.data存在则调用initData()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)}}现在让我们看看initData函数它做了什么接收当前初始化的vm实例functioninitData(vm:Component){letdata=vm.$options.datadata=vm._data=typeofdata==='function'?getData(数据,虚拟机):数据||{}if(!isPlainObject(data)){data={}process.env.NODE_ENV!=='production'&&warn('datafunctionsshouldreturnanobject:\n'+'https://vuejs.org/v2/指南/组件nts.html#data-Must-Be-a-Function',vm)}//在实例上代理数据constkeys=Object.keys(data)constprops=vm.$options.propsconstmethods=vm.$options.方法让我=键。lengthwhile(i--){constkey=keys[i]if(process.env.NODE_ENV!=='production'){if(methods&&hasOwn(methods,key)){warn(`Method"${key}"已经被定义为数据属性。`,vm)}}if(props&&hasOwn(props,key)){process.env.NODE_ENV!=='production'&&warn(`数据属性"${key}"已经声明为prop。`+`使用prop默认值代替。`,vm)}elseif(!isReserved(key)){proxy(vm,`_data`,key)}}//观察数据observe(data,true/*asRootData*/)}可以看到,它首先判断data是函数还是对象,如果data不是对象,function会给data赋null值,然后warn之后可以看到在while循环中,vue对数据中的key-value、props、methods是否重复命名做了判断。如果没有重复,就会执行proxy(vm,`_data`,key)什么是proxy?让我们来看看这个函数做了什么。exportfunctionproxy(target:Object,sourceKey:string,key:string){sharedPropertyDefinition.get=functionproxyGetter(){returnthis[sourceKey][key]}sharedPropertyDefinition.set=functionproxySetter(val){this[sourceKey][}key]=val}Object.defineProperty(target,key,sharedPropertyDefinition)}可以看到代理函数接收三个参数,第一个参数是vue的实例,第二个参数是sourceKey,即_data的第三个参数iskeyvalue就是数据对应的key值。它接收这三个键值并在当前vue实例的属性上定义一个_data键值。值为sharedPropertyDefinition。value是sharedPropertyDefinition有get和set方法,所以当我们调用this.data的时候,其实就相当于调用this._data.data(set是一样的)data(){return{a:'3'}}//bottom层实现Object.defineProperty(target,key,sharedPropertyDefinition)Object.defineProperty(vm,a,{get(val){returnthis["_data"]["a"]},set(val){this["_data"][“a”]=val}})