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

为什么vue2子组件中定义的数据一定要是一个函数

时间:2023-04-01 12:45:20 vue.js

需求是什么:现在我们需要在一个组件中定义一个name变量,并在界面上显示这个变量,代码:那么问题来了,为什么组件中的数据需要是一个功能?,数据可以声明为对象,这样不是更直接吗?代码:如果是按上面的方式写,不会出现万一不小心,在vscodedata处会出现一条红线报错datapropertyincomponentmustbeafunction,提醒我们datamustbeafunction。为什么好,重点来了,为什么?原因是:通过函数返回数据对象,保证了每个组件实例都有唯一的数据副本,避免了组件之间数据的相互影响。先说结论:每个子组件注册后,都会被处理成一个VueComponent实例,类似下面的MyVueComponent(...),data属性会挂在它的原型链上。当子组件中的数据为Object时,代码:functionMyVueComponent(){}MyVueComponent.prototype.data={name:'jack',age:22,}constcomponentA=newMyVueComponent();constcomponentB=newMyVueComponent();componentA.data.age=55;控制台。log(componentA.data.age,componentB.data.age)//55,55可以看到当componentA.data.age赋值为55时,componentB.data.age也改为55作为数据子组件为函数时,代码:;constcomponentB=newMyVueComponent();componentA.data.age=55;console.log(componentA.data.age,componentB.data.age)//可以看到55、22,componentA.data.age改变后,不会影响到componentB.data.age的值,也就是我们上面说的,避免了组件之间数据的相互影响注:为了避免组件中数据的相互影响,除了将数据定义为函数,还需要在data中返回一个全新的Object(有独立的内存地址),我们知道js中对象的赋值是通过引用传递的,代码:constmyData={age:22}functionMyVueComponent(){这个.data=this.data()}MyVueComponent.prototype.data=function(){returnmyData}constcomponentA=newMyVueComponent();constcomponentB=newMyVueComponent();componentA.data.age=55;console.log(componentA.data.age,componentB.data.age)//55,55,可以看到虽然我们把data定义为一个函数,但是data中返回的是对象myData,这里是引用传递,后面修改data中的值的时候,实际上修改了同一个内存地址的数据,所以还是有数据相互影响的再看细节:当我们需要在父组件中使用子组件时,以全局子组件为例,代码://app.vue//main.jsimportVuefrom'vue/dist/vue.esm.js'importAppfrom'./App.vue'Vue.component('app',App)newVue({el:'#app',template:''})首先,执行Vue.component(...)注册子组件,调用Vue.component()方法。看一下Vue.component()的内部实现:=function(id,definition){if(!definition){returnthis.options[type+'s'][id]}else{/*伊斯坦布尔忽略if*/if(process.env.NODE_ENV!=='production'&&type==='component'){validateComponentName(id);}//关键逻辑if(type==='component'&&isPlainObject(definition)){definition.name=definition.name||ID;definition=this.options._base.extend(definition);}//...省略一些不相关的代码this.options[type+'s'][id]=definition;返回定义}};});}Vue在初始化的时候会执行initAssetRegisters(...)方法,然后Vue.component(...)就出来了。定义参数就是我们上面例子调用Vue.component(...)传入的第二个参数,即:{name:'App',data(){return{name:'haha'}}}然后会执行definition=this.options._base.extend(definition)会调用Vue.extend(...)处理从外部传入的对象,最终构造一个VueComponent实例对象。当然我们传入的数据对象也挂在里面了,在浏览器调试器里看流程数据会更清楚。代码:Vue.extend=function(extendOptions){extendOptions=extendOptions||{};变种超级=这个;varSuperId=Super.cid;varcachedCtors=extendOptions._Ctor||(extendOptions._Ctor={});如果(cachedCtors[SuperId]){returncachedCtors[SuperId]}varname=extendOptions.name||超级选项名称;if(process.env.NODE_ENV!=='production'&&name){validateComponentName(name);}varSub=functionVueComponent(options){this._init(options);};Sub.prototype=Object.create(Super.prototype);Sub.prototype.constructor=Sub;sub.cid=cid++;Sub.options=mergeOptions(Super.options,extendOptions);子['超级']=超级;ASSET_TYPES.forEach(function(type){Sub[type]=Super[type];});如果(名称){Sub.options.components[name]=Sub;}Sub.superOptions=Super.options;Sub.extendOptions=extendOptions;Sub.sealedOptions=extend({},子选项);cachedCtors[SuperId]=Sub;返回子};接下来,执行newVue(...)代码:用`new`关键字调用')}this._init(options)}执行顺序:this._init(...)->initState(vm),即在initState(vm)方法中,data数据在该组件将被初始化,请参见以下代码:._data={},true/*asRootData*/)}}查看initData(...)方法的内部实现:functioninitData(vm:Component){letdata=vm.$options.datadata=vm._data=typeofdata==='函数'?getData(数据,虚拟机):数据||{}if(!isPlainObject(data)){data={}process.env.NODE_ENV!=='production'&&warn('数据函数应该返回一个对象:\n'+'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',vm)}//...省略一些不相关的代码}可以看到第二行代码会先判断data是否是一个函数?如果是函数,则调用getData(...)方法获取数据;如果不是函数,直接获取组件中的data数据。那么数据在什么情况下不是函数呢?当我们使用newVue(...)时,实例组件(通常是根节点)将不会被重用。因此,不存在组件中数据相互影响的问题。