今天小编给大家分享一下Vue2能通过this访问各种选项中属性的原因是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。万事开头难,找到起点是最难的,对于前端项目,我们想要找到入口文件,一般都是从package.json
中的main
字段开始找;package.json
中的main
字段代表的是这个包的入口文件,通常我们可以通过这个字段的值来找到我们要阅读的起点。但是对于Vue
来说,这个字段是dist/vue.runtime.common.js
,这个文件是编译后的文件,我们是看不懂的,所以需要找到源码的入口文件;这个时候我们就需要看package.json
中的scripts
字段:
{ "scripts":{ "dev":"rollup-w-cscripts/config.js--environmentTARGET:full-dev", "dev:cjs":"rollup-w-cscripts/config.js--environmentTARGET:runtime-cjs-dev", "dev:esm":"rollup-w-cscripts/config.js--environmentTARGET:runtime-esm", "dev:ssr":"rollup-w-cscripts/config.js--environmentTARGET:server-renderer", "dev:compiler":"rollup-w-cscripts/config.js--environmentTARGET:compiler", "build":"nodescripts/build.js", "build:ssr":"npmrunbuild--runtime-cjs,server-renderer", "build:types":"rimraftemp&&tsc--declaration--emitDeclarationOnly--outDirtemp&&api-extractorrun&&api-extractorrun-cpackages/compiler-sfc/api-extractor.json", "test":"npmrunts-check&&npmruntest:types&&npmruntest:unit&&npmruntest:e2e&&npmruntest:ssr&&npmruntest:sfc", "test:unit":"vitestruntest/unit", "test:ssr":"npmrunbuild:ssr&&vitestrunserver-renderer", "test:sfc":"vitestruncompiler-sfc", "test:e2e":"npmrunbuild--full-prod,server-renderer-basic&&vitestruntest/e2e", "test:transition":"karmastarttest/transition/karma.conf.js", "test:types":"npmrunbuild:types&&tsc-p./types/tsconfig.json", "format":"prettier--write--parsertypescript"(src|test|packages|types)/**/*.ts"", "ts-check":"tsc-ptsconfig.json--noEmit", "ts-check:test":"tsc-ptest/tsconfig.json--noEmit", "bench:ssr":"npmrunbuild:ssr&&nodebenchmarks/ssr/renderToString.js&&nodebenchmarks/ssr/renderToStream.js", "release":"nodescripts/release.js", "changelog":"conventional-changelog-pangular-iCHANGELOG.md-s" } }
可以看到Vue
的package.json
中有很多的scripts
,这些相信大家都可以看得懂,这里我们只关注dev
和build
这两个脚本;dev
脚本是用来开发的,build
脚本是用来打包的,我们可以看到dev
脚本中有一个TARGET
的环境变量,这个环境变量的值是full-dev
,我们可以在scripts/config.js
中找到这个值;直接在scripts/config.js
中搜索full-dev
:这样就可以找到这个值对应的配置:
varconfig={ 'full-dev':{ entry:resolve('web/entry-runtime-with-compiler.ts'), dest:resolve('dist/vue.js'), format:'umd', env:'development', alias:{he:'./entity-decoder'}, banner } }
entry
字段就是我们要找的入口文件,这个文件就是Vue
的源码入口文件,后面的值是web/entry-runtime-with-compiler.ts
,我们可以在web
目录下找到这个文件;但是并没有在根目录下找到web
目录,这个时候我们就大胆猜测,是不是有别名配置,这个时候我也正好在scripts
下看到了一个alias.js
文件,打开这个文件,发现里面有一个web
的别名;代码如下:
module.exports={ vue:resolve('src/platforms/web/entry-runtime-with-compiler'), compiler:resolve('src/compiler'), core:resolve('src/core'), web:resolve('src/platforms/web'), weex:resolve('src/platforms/weex'), shared:resolve('src/shared') }
为了验证我们的猜测,我们可以在config.js
中搜一下alias
,发现确实有引入这个文件:
constaliases=require('./alias') constresolve=p=>{ constbase=p.split('/')[0] if(aliases[base]){ returnpath.resolve(aliases[base],p.slice(base.length+1)) }else{ returnpath.resolve(__dirname,'../',p) } }
再搜一下aliases
,发现确实有配置别名:
//省略部分代码 constconfig={ plugins:[ alias({ entries:Object.assign({},aliases,opts.alias) }), ].concat(opts.plugins||[]), }
这样我们就可以确认,web
就是src/platforms/web
这个目录,我们可以在这个目录下找到entry-runtime-with-compiler.ts
这个文件;这样我们就成功的找到了Vue
的源码入口文件,接下来我们就可以开始阅读源码了;上面找到了入口文件,但是还是不知道如何阅读源码,这个时候我们就需要一些技巧了,这里我就分享一下我自己的阅读源码的技巧;像我们现在看的源码几乎都是使用esm
模块化或者commonjs
模块化的,这些都会有一个export
或者module.exports
,我们可以通过这个来看导出了什么;只看导出的内容,其他的暂时不用管,直接找到最终导出的内容,例如Vue
的源码:entry-runtime-with-compiler.ts
的导出内容:
importVuefrom'./runtime-with-compiler' exportdefaultVue
这个时候就去找runtime-with-compiler.ts
的导出内容:runtime-with-compiler.ts
的导出内容:
importVuefrom'./runtime/index' exportdefaultVueasGlobalAPI
这个时候就去找runtime/index.ts
的导出内容:runtime/index.ts
的导出内容:
importVuefrom'core/index' exportdefaultVue
这个时候就去找core/index.ts
的导出内容:core/index.ts
的导出内容:
importVuefrom'./instance/index' exportdefaultVue
这个时候就去找instance/index.ts
的导出内容:instance/index.ts
的导出内容:
functionVue(options){ if(__DEV__&&!(thisinstanceofVue)){ warn('Vueisaconstructorandshouldbecalledwiththe`new`keyword') } this._init(options) } exportdefaultVueasunknownasGlobalAPI
这样我们就找到Vue
的构造函数了,这个时候我们就可以开始阅读源码了;阅读源码的目的一定要清晰,当然你可以说目的就是了解Vue
的实现原理,但是这个目的太宽泛了,我们可以把目的细化一下,例如:Vue
的生命周期是怎么实现的Vue
的数据响应式是怎么实现的Vue
的模板编译是怎么实现的Vue
的组件化是怎么实现的Vue
的插槽是怎么实现的等等…例如我们的这次阅读计划就是了解Vue
的this
为什么可以访问到选项中的各种属性,这里再细分为:Vue
的this
是怎么访问到data
的Vue
的this
是怎么访问到methods
的Vue
的this
是怎么访问到computed
的Vue
的this
是怎么访问到props
的上面顺序不分先后,但是答案一定是在源码中。上面已经找到了Vue
的入口文件,接下来我们就可以开始阅读源码了,这里我就以Vue
的this
为什么可以访问到选项中的各种属性为例,来分析Vue
的源码;首先看一下instance/index.ts
的源码:
import{initMixin}from'./init' import{stateMixin}from'./state' import{renderMixin}from'./render' import{eventsMixin}from'./events' import{lifecycleMixin}from'./lifecycle' import{warn}from'../util/index' importtype{GlobalAPI}from'types/global-api' functionVue(options){ if(__DEV__&&!(thisinstanceofVue)){ warn('Vueisaconstructorandshouldbecalledwiththe`new`keyword') } this._init(options) } //@ts-expect-errorVuehasfunctiontype initMixin(Vue) //@ts-expect-errorVuehasfunctiontype stateMixin(Vue) //@ts-expect-errorVuehasfunctiontype eventsMixin(Vue) //@ts-expect-errorVuehasfunctiontype lifecycleMixin(Vue) //@ts-expect-errorVuehasfunctiontype renderMixin(Vue) exportdefaultVueasunknownasGlobalAPI
有这么多东西,我们不用管,要清晰目的,我们在使用Vue
的时候,通常是下面这样的:
constvm=newVue({ data(){ return{ msg:'helloworld' } }, methods:{ say(){ console.log(this.msg) } } }); vm.say();
也就是Vue
的构造函数接收一个选项对象,这个选项对象中有data
和methods
;我们要知道Vue
的this
为什么可以访问到data
和methods
,那么我们就要找到Vue
的构造函数中是怎么把data
和methods
挂载到this
上的;很明显构造函数只做了一件事,就是调用了this._init(options)
:
this._init(options)
那么我们就去找_init
方法,这个方法在哪我们不知道,但是继续分析源码,我们可以看到下面会执行很多xxxMixin
的函数,并且Vue
作为参数传入:
//@ts-expect-errorVuehasfunctiontype initMixin(Vue) //@ts-expect-errorVuehasfunctiontype stateMixin(Vue) //@ts-expect-errorVuehasfunctiontype eventsMixin(Vue) //@ts-expect-errorVuehasfunctiontype lifecycleMixin(Vue) //@ts-expect-errorVuehasfunctiontype renderMixin(Vue)
盲猜一波,见名知意:initMixin
:初始化混入stateMixin
:状态混入eventsMixin
:事件混入lifecycleMixin
:生命周期混入renderMixin
:渲染混入我们就去找这些混入的方法,一个一个的找,找到initMixin
,直接就找了_init
方法:
exportfunctioninitMixin(Vue:typeofComponent){ Vue.prototype._init=function(options?:Record){ constvm:Component=this //auid vm._uid=uid++ letstartTag,endTag /*istanbulignoreif*/ if(__DEV__&&config.performance&&mark){ startTag=`vue-perf-start:${vm._uid}` endTag=`vue-perf-end:${vm._uid}` mark(startTag) } //aflagtomarkthisasaVueinstancewithouthavingtodoinstanceof //check vm._isVue=true //avoidinstancesfrombeingobserved vm.__v_skip=true //effectscope vm._scope=newEffectScope(true/*detached*/) vm._scope._vm=true //mergeoptions if(options&&options._isComponent){ //optimizeinternalcomponentinstantiation //sincedynamicoptionsmergingisprettyslow,andnoneofthe //internalcomponentoptionsneedsspecialtreatment. initInternalComponent(vm,optionsasany) }else{ vm.$options=mergeOptions( resolveConstructorOptions(vm.constructorasany), options||{}, vm ) } /*istanbulignoreelse*/ if(__DEV__){ initProxy(vm) }else{ vm._renderProxy=vm } //exposerealself vm._self=vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm,'beforeCreate',undefined,false/*setContext*/) initInjections(vm)//resolveinjectionsbeforedata/props initState(vm) initProvide(vm)//resolveprovideafterdata/props callHook(vm,'created') /*istanbulignoreif*/ if(__DEV__&&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) } } }
代码这么多没必要全都看,记住我们的目的是找到data
和methods
是怎么挂载到this
上的;先简化代码,不看没有意义的代码:
exportfunctioninitMixin(Vue){ Vue.prototype._init=function(options){ constvm=this } }
传递过来的Vue
并没有做太多事情,只是把_init
方法挂载到了Vue.prototype
上;在_init
方法中,vm
被赋值为this
,这里的this
就是Vue
的实例,也就是我们的vm
;继续往下看,我们有目的的看代码,只需要看有vm
和options
组合出现的代码,于是就看到了:
if(options&&options._isComponent){ initInternalComponent(vm,options) }else{ vm.$options=mergeOptions( resolveConstructorOptions(vm.constructor), options||{}, vm ) }
_isComponent
前面带有_
,说明是私有属性,我们通过new Vue
创建的实例时走到现在是没有这个属性的,所以走到else
分支;resolveConstructorOptions(vm.constructor)
中没有传递options
,所以不看这个方法,直接看mergeOptions
:
exportfunctionmergeOptions(parent,child,vm){ if(__DEV__){ checkComponents(child) } if(isFunction(child)){ //@ts-expect-error child=child.options } normalizeProps(child,vm) normalizeInject(child,vm) normalizeDirectives(child) //Applyextendsandmixinsonthechildoptions, //butonlyifitisarawoptionsobjectthatisn't //theresultofanothermergeOptionscall. //Onlymergedoptionshasthe_baseproperty. if(!child._base){ if(child.extends){ parent=mergeOptions(parent,child.extends,vm) } if(child.mixins){ for(leti=0,l=child.mixins.length;i
记住我们的目的,只需要关心vm
和options
组合出现的代码,child
就是options
,vm
就是vm
,简化之后:
exportfunctionmergeOptions(parent,child,vm){ normalizeProps(child,vm) normalizeInject(child,vm) normalizeDirectives(child) returnoptions }
可以看到只剩下了normalizeProps
、normalizeInject
、normalizeDirectives
这三个方法,值得我们关注,但是见名知意,这三个方法可能并不是我们想要的,跟进去看一眼也确实不是;虽然没有得到我们想要的,但是从这里我们也得到了一个重要信息,mergeOptions
最后会返回一个options
对象,这个对象就是我们的options
,最后被vm.$options
接收;
vm.$options=mergeOptions( resolveConstructorOptions(vm.constructor), options||{}, vm )
现在我们分析要多一步了,参数只有vm
的函数也是需要引起我们的注意的,继续往下看:
if(__DEV__){ initProxy(vm) }else{ vm._renderProxy=vm }
操作了vm
,但是内部没有操作$options
,跳过,继续往下看:
initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm,'beforeCreate',undefined,false/*setContext*/) initInjections(vm)//resolveinjectionsbeforedata/props initState(vm) initProvide(vm)//resolveprovideafterdata/props callHook(vm,'created')
initLifecycle
、initEvents
、initRender
、initInjections
、initState
、initProvide
这些方法都是操作vm
的;盲猜一波:initLifecycle
:初始化生命周期initEvents
:初始化事件initRender
:初始化渲染initInjections
:初始化注入initState
:初始化状态initProvide
:初始化依赖注入callHook
:调用钩子这里面最有可能是我们想要的是initState
,跟进去看一下:
exportfunctioninitState(vm){ constopts=vm.$options if(opts.props)initProps(vm,opts.props) //CompositionAPI initSetup(vm) if(opts.methods)initMethods(vm,opts.methods) if(opts.data){ initData(vm) }else{ constob=observe((vm._data={})) ob&&ob.vmCount++ } if(opts.computed)initComputed(vm,opts.computed) if(opts.watch&&opts.watch!==nativeWatch){ initWatch(vm,opts.watch) } }
已经找到我们想要的了,现在开始正式分析initState
。根据代码结构可以看到,initState
主要做了以下几件事:初始化props
初始化setup
初始化methods
初始化data
初始化computed
初始化watch
我们可以用this
来访问的属性是props
、methods
、data
、computed
;看到这里也明白了,为什么在props
中定义了一个属性,在data
、methods
、computed
中就不能再定义了,因为props
是最先初始化的,后面的也是同理。initProps
的作用是初始化props
,跟进去看一下:
functioninitProps(vm,propsOptions){ constpropsData=vm.$options.propsData||{} constprops=(vm._props=shallowReactive({})) //cachepropkeyssothatfuturepropsupdatescaniterateusingArray //insteadofdynamicobjectkeyenumeration. constkeys=(vm.$options._propKeys=[]) constisRoot=!vm.$parent //rootinstancepropsshouldbeconverted if(!isRoot){ toggleObserving(false) } for(constkeyinpropsOptions){ keys.push(key) constvalue=validateProp(key,propsOptions,propsData,vm) /*istanbulignoreelse*/ if(__DEV__){ consthyphenatedKey=hyphenate(key) if( isReservedAttribute(hyphenatedKey)|| config.isReservedAttr免费云主机域名(hyphenatedKey) ){ warn( `"${hyphenatedKey}"isareservedattributeandcannotbeusedascomponentprop.`, vm ) } defineReactive(props,key,value,()=>{ if(!isRoot&&!isUpdatingChildComponent){ warn( `Avoidmutatingapropdirectlysincethevaluewillbe`+ `overwrittenwhenevertheparentcomponentre-renders.`+ `Instead,useadataorcomputedpropertybasedontheprop's`+ `value.Propbeingmutated:"${key}"`, vm ) } }) }else{ defineReactive(props,key,value) } //staticpropsarealreadyproxiedonthecomponent'sprototype //duringVue.extend().Weonlyneedtoproxypropsdefinedat //instantiationhere. if(!(keyinvm)){ proxy(vm,`_props`,key) } } toggleObserving(true) }
代码很多,我们依然不用关心其他的代码,只关心props
是怎么挂载到vm
上的,根据我上面的方法,简化后的代码如下:
functioninitProps(vm,propsOptions){ vm._props=shallowReactive({}) for(constkeyinpropsOptions){ constvalue=validateProp(key,propsOptions,propsData,vm) if(!(keyinvm)){ proxy(vm,`_props`,key) } } }
这里真正有关的就两个地方:validateProp
:看名字就知道是验证props
,跳过proxy
:代理,很可疑,跟进去看一下:
exportfunctionproxy(target,sourceKey,key){ sharedPropertyDefinition.get=functionproxyGetter(){ returnthis[sourceKey][key] } sharedPropertyDefinition.set=functionproxySetter(val){ this[sourceKey][key]=val } Object.defineProperty(target,key,sharedPropertyDefinition) }
这里的target
就是vm
,sourceKey
就是_props
,key
就是props
的属性名;这里通过Object.defineProperty
把vm
的属性代理到_props
上,这样就可以通过this
访问到props
了。不是很好理解,那我们来自己就用这些代码实现一下:
varoptions={ props:{ name:{ type:String, default:'defaultname' } } } functionVue(options){ constvm=this initProps(vm,options.props) } functioninitProps(vm,propsOptions){ vm._props={} for(constkeyinpropsOptions){ proxy(vm,`_props`,key) } } functionproxy(target,sourceKey,key){ Object.defineProperty(target,key,{ get(){ returnthis[sourceKey][key] }, set(val){ this[sourceKey][key]=val } }) } constvm=newVue(options) console.log(vm.name); console.log(vm._props.name); vm.name='name' console.log(vm.name); console.log(vm._props.name);
上面的代码只是为了方便理解,所以会忽略一些细节,比如props
的验证等等,真实挂载在_props
上的props
是通过defineReactive
实现的,我这里直接是空的,这些超出了本文的范围。initMethods
的代码如下:
functioninitMethods(vm,methods){ constprops=vm.$options.props for(constkeyinmethods){ if(__DEV__){ if(typeofmethods[key]!=='function'){ warn( `Method"${key}"hastype"${typeofmethods[ key ]}"inthecomponentdefinition.`+ `Didyoureferencethefunctioncorrectly?`, vm ) } if(props&&hasOwn(props,key)){ warn(`Method"${key}"hasalreadybeendefinedasaprop.`,vm) } if(keyinvm&&isReserved(key)){ warn( `Method"${key}"conflictswithanexistingVueinstancemethod.`+ `Avoiddefiningcomponentmethodsthatstartwith_or$.` ) } } vm[key]=typeofmethods[key]!=='function'?noop:bind(methods[key],vm) } }
跟着之前的思路,我们忽略无关代码,简化后的代码如下:
functioninitMethods(vm,methods){ for(constkeyinmethods){ vm[key]=typeofmethods[key]!=='function'?noop:bind(methods[key],vm) } }
这里的vm[key]
就是methods
的方法,这样就可以通过this
访问到methods
中定义的方法了。bind
的作用是把methods
中定义的函数的this
指向vm
,这样就可以在methods
中使用this
就是vm
了。简单的实现一下:
varoptions={ methods:{ say(){ console.log('say'); } } } functionVue(options){ constvm=this initMethods(vm,options.methods) } functioninitMethods(vm,methods){ for(constkeyinmethods){ vm[key]=typeofmethods[key]!=='function'?noop:bind(methods[key],vm) } } functionnoop(){} functionpolyfillBind(fn,ctx){ functionboundFn(a){ constl=arguments.length returnl ?l>1 ?fn.apply(ctx,arguments) :fn.call(ctx,a) :fn.call(ctx) } boundFn._length=fn.length returnboundFn } functionnativeBind(fn,ctx){ returnfn.bind(ctx) } constbind=Function.prototype.bind?nativeBind:polyfillBind constvm=newVue(options) vm.say()
initData
的代码如下:
functioninitData(vm){ letdata=vm.$options.data data=vm._data=isFunction(data)?getData(data,vm):data||{} if(!isPlainObject(data)){ data={} __DEV__&& warn( 'datafunctionsshouldreturnanobject:n'+ 'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ) } //proxydataoninstance constkeys=Object.keys(data) constprops=vm.$options.props constmethods=vm.$options.methods leti=keys.length while(i--){ constkey=keys[i] if(__DEV__){ if(methods&&hasOwn(methods,key)){ warn(`Method"${key}"hasalreadybeendefinedasadataproperty.`,vm) } } if(props&&hasOwn(props,key)){ __DEV__&& warn( `Thedataproperty"${key}"isalreadydeclaredasaprop.`+ `Usepropdefaultvalueinstead.`, vm ) }elseif(!isReserved(key)){ proxy(vm,`_data`,key) } } //observedata constob=observe(data) ob&&ob.vmCount++ }
简化之后的代码如下:
functioninitData(vm){ letdata=vm.$options.data //proxydataoninstance constkeys=Object.keys(data) leti=keys.length while(i--){ constkey=keys[i] proxy(vm,`_data`,key) } }
这里的实现方式和initProps
是一样的,都是通过proxy
把data
中的属性代理到vm
上。注意:initData
的获取值的地方是其他的不相同,这里只做提醒,不做详细分析。initComputed
的代码如下:
functioninitComputed(vm,computed){ //$flow-disable-line constwatchers=(vm._computedWatchers=Object.create(null)) //computedpropertiesarejustgettersduringSSR constisSSR=isServerRendering() for(constkeyincomputed){ constuserDef=computed[key] constgetter=isFunction(userDef)?userDef:userDef.get if(__DEV__&&getter==null){ warn(`Getterismissingforcomputedproperty"${key}".`,vm) } if(!isSSR){ //createinternalwatcherforthecomputedproperty. watchers[key]=newWatcher( vm, getter||noop, noop, computedWatcherOptions ) } //component-definedcomputedpropertiesarealreadydefinedonthe //componentprototype.Weonlyneedtodefinecomputedpropertiesdefined //atinstantiationhere. if(!(keyinvm)){ defineComputed(vm,key,userDef) }elseif(__DEV__){ if(keyinvm.$data){ warn(`Thecomputedproperty"${key}"isalreadydefinedindata.`,vm) }elseif(vm.$options.props&&keyinvm.$options.props){ warn(`Thecomputedproperty"${key}"isalreadydefinedasaprop.`,vm) }elseif(vm.$options.methods&&keyinvm.$options.methods){ warn( `Thecomputedproperty"${key}"isalreadydefinedasamethod.`, vm ) } } } }
简化之后的代码如下:
functioninitComputed(vm,computed){ for(constkeyincomputed){ constuserDef=computed[key] constgetter=userDef defineComputed(vm,key,userDef) } }
这里的实现主要是通过defineComputed
来定义computed
属性,进去瞅瞅:
exportfunctiondefineComputed(target,key,userDef){ constshouldCache=!isServerRendering() if(isFunction(userDef)){ sharedPropertyDefinition.get=shouldCache ?createComputedGetter(key) :createGetterInvoker(userDef) sharedPropertyDefinition.set=noop }else{ sharedPropertyDefinition.get=userDef.get ?shouldCache&&userDef.cache!==false ?createComputedGetter(key) :createGetterInvoker(userDef.get) :noop sharedPropertyDefinition.set=userDef.set||noop } if(__DEV__&&sharedPropertyDefinition.set===noop){ sharedPropertyDefinition.set=function(){ warn( `Computedproperty"${key}"wasassignedtobutithasnosetter.`, this ) } } Object.defineProperty(target,key,sharedPropertyDefinition) }
仔细看下来,其实实现方式还是和initProps
和initData
一样,都是通过Object.defineProperty
来定义属性;不过里面的getter
和setter
是通过createComputedGetter
和createGetterInvoker
来创建的,这里不做过多分析。上面我们已经分析了props
、methods
、data
、computed
的属性为什么可以直接通过this
来访问,那么我们现在就来实现一下这个功能。上面已经简单了实现了initProps
、initMethods
,而initData
和initComputed
的实现方式和initProps
的方式一样,所以我们直接复用就好了:
functionVue(options){ this._init(options) } Vue.prototype._init=function(options){ constvm=this vm.$options=options initState(vm) } functioninitState(vm){ constopts=vm.$options if(opts.props)initProps(vm,opts.props) if(opts.methods)initMethods(vm,opts.methods) if(opts.data)initData(vm) if(opts.computed)initComputed(vm,opts.computed) } functioninitProps(vm,propsOptions){ vm._props={} for(constkeyinpropsOptions){ vm._props[key]=propsOptions[key].default proxy(vm,`_props`,key) } } functionproxy(target,sourceKey,key){ Object.defineProperty(target,key,{ get(){ returnthis[sourceKey][key] }, set(val){ this[sourceKey][key]=val } }) } functioninitMethods(vm,methods){ for(constkeyinmethods){ vm[key]=typeofmethods[key]!=='function'?noop:bind(methods[key],vm) } } functionnoop(){} functionpolyfillBind(fn,ctx){ functionboundFn(a){ constl=arguments.length returnl ?l>1 ?fn.apply(ctx,arguments) :fn.call(ctx,a) :fn.call(ctx) } boundFn._length=fn.length returnboundFn } functionnativeBind(fn,ctx){ returnfn.bind(ctx) } constbind=Function.prototype.bind?nativeBind:polyfillBind functioninitData(vm){ vm._data={} for(constkeyinvm.$options.data){ vm._data[key]=vm.$options.data[key] proxy(vm,`_data`,key) } } functioninitComputed(vm,computed){ for(constkeyincomputed){ constuserDef=computed[key] constgetter=userDef defineComputed(vm,key,bind(userDef,vm)) } } functiondefineComputed(target,key,userDef){ Object.defineProperty(target,key,{ get(){ returnuserDef() }, }) } constvm=newVue({ props:{ a:{ type:String, default:'default' } }, data:{ b:1 }, methods:{ c(){ console.log(this.b) } }, computed:{ d(){ returnthis.b+1 } } }) console.log('propsa:default',vm.a) console.log('datab:1',vm.b) vm.c()//1 console.log('computedd:2',vm.d)
注意:上面的代码对比于文章中写的示例有改动,主要是为了实现最后打印结果正确,增加了赋值操作。以上就是“Vue2能通过this访问各种选项中属性的原因是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注百云主机行业资讯频道。
这篇文章主要介绍了vue的v-model是什么及怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue的v-model是什么及怎么使用文章都会有所收获,下面我们一起来看看吧。v-model 是Vue框架的一种内置的API指令…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。