原因:防止多個組件實(shí)例對象之間共用一個data,產(chǎn)生數(shù)據(jù)污染;采用函數(shù)的形式,initData時會將其作為工廠函數(shù)都會返回全新data對象。當(dāng)將組件中的data寫成一個函數(shù),數(shù)據(jù)以函數(shù)返回值形式定義,這樣每復(fù)用一次組件,就會返回一份新的data,擁有自己的作用域,類似于給每個組件實(shí)例創(chuàng)建一個私有的數(shù)據(jù)空間,讓各個組件實(shí)例維護(hù)各自的數(shù)據(jù)。
前端(vue)入門到精通課程,老師在線輔導(dǎo):聯(lián)系老師
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點(diǎn)擊使用
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
一、實(shí)例和組件定義data的區(qū)別
vue實(shí)例的時候定義data屬性既可以是一個對象,也可以是一個函數(shù)
const app = new Vue({ el:"#app", // 對象格式 data:{ foo:"foo" }, // 函數(shù)格式 data(){ return { foo:"foo" } } })
組件中定義data屬性,只能是一個函數(shù)
如果為組件data直接定義為一個對象
Vue.component('component1',{ template:`<div>組件</div>`, data:{ foo:"foo" }})
則會得到警告信息
警告說明:返回的data應(yīng)該是一個函數(shù)在每一個組件實(shí)例中
二、組件data定義函數(shù)與對象的區(qū)別
上面講到組件data必須是一個函數(shù),不知道大家有沒有思考過這是為什么呢?
在我們定義好一個組件的時候,vue最終都會通過Vue.extend()構(gòu)成組件實(shí)例
這里我們模仿組件構(gòu)造函數(shù),定義data屬性,采用對象的形式
function Component(){ } Component.prototype.data = { count : 0 }
創(chuàng)建兩個組件實(shí)例
const componentA = new Component() const componentB = new Component()
修改componentA組件data屬性的值,componentB中的值也發(fā)生了改變
console.log(componentB.data.count) // 0 componentA.data.count = 1 console.log(componentB.data.count) // 1
產(chǎn)生這樣的原因這是兩者共用了同一個內(nèi)存地址,componentA修改的內(nèi)容,同樣對componentB產(chǎn)生了影響?!緦W(xué)習(xí)視頻分享:vue視頻教程、web前端視頻】
如果我們采用函數(shù)的形式,則不會出現(xiàn)這種情況(函數(shù)返回的對象內(nèi)存地址并不相同)
function Component(){ this.data = this.data() } Component.prototype.data = function (){ return { count : 0 } }
修改componentA組件data屬性的值,componentB中的值不受影響
console.log(componentB.data.count) // 0 componentA.data.count = 1 console.log(componentB.data.count) // 0
vue組件可能會有很多個實(shí)例,采用函數(shù)返回一個全新data形式,使每個實(shí)例對象的數(shù)據(jù)不會受到其他實(shí)例對象數(shù)據(jù)的污染
三、原理分析
首先可以看看vue初始化data的代碼,data的定義可以是函數(shù)也可以是對象
源碼位置:/vue-dev/src/core/instance/state.js
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} ... }
data既能是object也能是function,那為什么還會出現(xiàn)上文警告呢?
別急,繼續(xù)看下文
組件在創(chuàng)建的時候,會進(jìn)行選項(xiàng)的合并
源碼位置:/vue-dev/src/core/util/options.js
自定義組件會進(jìn)入mergeOptions進(jìn)行選項(xiàng)合并
Vue.prototype._init = function (options?: Object) { ... // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } ... }
定義data會進(jìn)行數(shù)據(jù)校驗(yàn)
源碼位置:/vue-dev/src/core/instance/init.js
這時候vm實(shí)例為undefined,進(jìn)入if判斷,若data類型不是function,則出現(xiàn)警告提示
strats.data = function ( parentVal: any, childVal: any, vm?: Component ): ?Function { if (!vm) { if (childVal && typeof childVal !== "function") { process.env.NODE_ENV !== "production" && warn( 'The "data" option should be a function ' + "that returns a per-instance value in component " + "definitions.", vm ); return parentVal; } return mergeDataOrFn(parentVal, childVal); } return mergeDataOrFn(parentVal, childVal, vm); };
四、結(jié)論
根實(shí)例對象data可以是對象也可以是函數(shù)(根實(shí)例是單例),不會產(chǎn)生數(shù)據(jù)污染情況
組件實(shí)例對象data必須為函數(shù),目的是為了防止多個組件實(shí)例對象之間共用一個data,產(chǎn)生數(shù)據(jù)污染。采用函數(shù)的形式,initData時會將其作為工廠函數(shù)都會返回全新data對象
說明:
-
vue中組件是用來復(fù)用的,為了防止data復(fù)用,將其定義為函數(shù)。
-
vue組件中的data數(shù)據(jù)都應(yīng)該是相互隔離,互不影響的,組件每復(fù)用一次,data數(shù)據(jù)就應(yīng)該被復(fù)制一次,之后,當(dāng)某一處復(fù)用的地方組件內(nèi)data數(shù)據(jù)被改變時,其他復(fù)用地方組件的data數(shù)據(jù)不受影響,就需要通過data函數(shù)返回一個對象作為組件的狀態(tài)。
-
當(dāng)我們將組件中的data寫成一個函數(shù),數(shù)據(jù)以函數(shù)返回值形式定義,這樣每復(fù)用一次組件,就會返回一份新的data,擁有自己的作用域,類似于給每個組件實(shí)例創(chuàng)建一個私有的數(shù)據(jù)空間,讓各個組件實(shí)例維護(hù)各自的數(shù)據(jù)。
-
當(dāng)我們組件的date單純的寫成對象形式,這些實(shí)例用的是同一個構(gòu)造函數(shù),由于JavaScript的特性所導(dǎo)致,所有的組件實(shí)例共用了一個data,就會造成一個變了全都會變的結(jié)果。
(學(xué)習(xí)視頻分享:web前端開發(fā)、編程基礎(chǔ)視頻)