Vue中的父子組件通訊以及使用sync同步父子組件數(shù)據(jù)
前言
父子組件通訊,可分為兩種情況:
1. 父組件向子組件中傳遞數(shù)據(jù)
2. 子組件向父組件中傳遞數(shù)據(jù)
一般情況下, 1中情況可通過props解決數(shù)據(jù)傳遞的問題, 這里就不多贅述了。
子組件向父組件中傳遞數(shù)據(jù)
主要談?wù)?中情景的實現(xiàn),有三種方式:
一. 通過props,父組件向子組件中傳遞數(shù)據(jù)和改變數(shù)據(jù)的函數(shù),通過在子組件中調(diào)用父組件傳過來的函數(shù),達到更新父組件數(shù)據(jù)(向父組件傳遞數(shù)據(jù))的作用(子組件中需要有相應(yīng)的響應(yīng)事件)
二. 通過在子組件中觸發(fā)一個 自定義事件(vm.$emit),將數(shù)據(jù)作為vm.$emit方法的參數(shù),回傳給父組件用v-on:[自定義事件]監(jiān)聽的函數(shù)
三.通過ref對子組件做標(biāo)記,父組件可以通過vm.$refs.[子組件的ref].[子組件的屬性/方法]這種方式直接取得子組件的數(shù)據(jù)
下面我將一 一展示
一. 通過props從父向子組件傳遞函數(shù),調(diào)用函數(shù)改變父組件數(shù)據(jù)
這里就不做代碼展示了
一來是因為相對比較簡單
二來是因為這種方式顯然不是Vue中的最佳實踐(在react中倒比較常見)
想要看代碼的話可以看這里:《【Vue】淺談Vue不同場景下組件間的數(shù)據(jù)交流》http://www.cnblogs.com/penghuwan/p/7286912.html (在兄弟組件的數(shù)據(jù)交流那一節(jié))
二. 通過自定義事件從子組件向父組件中傳遞數(shù)據(jù)
我們可以在子組件中通過$emit(event, [...參數(shù)])觸發(fā)一個自定義的事件,這樣,父組件可以在使用子組件的地方直接用 v-on來監(jiān)聽子組件觸發(fā)的事件, 并且可以在監(jiān)聽函數(shù)中依次取得所有從子組件傳來的參數(shù)
例如:
在子組件中某個部分寫入:
this.emit('eventYouDefined', arg);
然后你就可以在父組件的子組件模板里監(jiān)聽:
// 這里是父組件的Template:
<Son v-on: eventYouDefined = "functionYours" />
下面是一個實例
父組件
<template>
<div id="father">
<div>
我是父組件,我接受到了:
{{ text || '暫無數(shù)據(jù)' }}
<son v-on:sendData='getSonText'></son>
</div>
</div>
</template>
<script>
import son from './son.vue'
export default {
data: function () {
return {
text: ''
}
},
components: {
son: son
},
methods: {
getSonText (text) {
this.text = text
}
}
}
</script>
<style scoped>
#father div {
padding: 10px;
margin: 10px;
border: 1px solid grey;
overflow: hidden;
}
</style>
子組件:
<template>
<div>
<p>我是子組件,我所擁有的數(shù)據(jù): {{ text }}</p>
<button @click="sendData">
發(fā)送數(shù)據(jù)
</button>
</div>
</template>
<script>
export default {
data () {
return {
text: '來自子組件的數(shù)據(jù)'
}
},
methods: {
sendData () {
this.$emit('sendData', this.text)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</style>
在點擊子組件中的“發(fā)送數(shù)據(jù)”按鈕前, 父組件還沒有接受到數(shù)據(jù)(text為空字符串), 則通過 {{ text || '暫無數(shù)據(jù)' }}將顯示默認文本:‘暫無數(shù)據(jù)'

點擊“發(fā)送數(shù)據(jù)”按鈕后:

因為sendData自定義事件被觸發(fā),通過
this.$emit('sendData', this.text) //此處的this指向子組件實例)
子組件的text數(shù)據(jù)被父組件中:
<son v-on:sendData='getSonText'></son>
中的getSonText函數(shù)作為參數(shù)接傳參受到, 從而完成了從子組件向父組件中的傳參過程
三. 通過ref屬性在父組件中直接取得子組件的數(shù)據(jù)(data)
對于我們上面講的一和二的處理情景來說,有個局限性就是它們都需要以事件機制為基礎(chǔ)(無論是像click那樣的原生事件還是自定義事件),而在事件發(fā)生的時候才能調(diào)用函數(shù)將數(shù)據(jù)傳遞過來
但如果子組件里沒有類似“按鈕”的東西,因而無法制造原生事件,同時也沒辦法找到一個觸發(fā)自定義事件的時機的時候,怎么從子組件向父組件傳遞數(shù)據(jù)呢??
這個時候, 我們就只能從父組件中“直接取”子組件的數(shù)據(jù)了,借助ref屬性
ref是我們經(jīng)常用到的Vue屬性,利用它可以簡單方便地從本組件的template中取得DOM實例,而實際上,如果你在父組件中為子組件設(shè)置ref的話, 就可以直接通過vm.$refs.[子組件的ref].[子組件的屬性]去拿到數(shù)據(jù)啦,例如:
父組件:
<template>
<div id="father">
<div>
我是父組件,我接受到了:
{{ text || '暫無數(shù)據(jù)' }}
<button @click="getSonText()">接受數(shù)據(jù)</button>
<son ref='son'></son>
</div>
</div>
</template>
<script>
import son from './son.vue'
export default {
data: function () {
return {
text: ''
}
},
components: {
son: son
},
methods: {
getSonText () {
this.text = this.$refs.son.text
}
}
}
</script>
<style scoped>
#father div {
padding: 10px;
margin: 10px;
border: 1px solid grey;
overflow: hidden;
}
</style>
子組件:
<template>
<div>
<p>我是子組件,我所擁有的數(shù)據(jù): {{ text }}</p>
</div>
</template>
<script>
export default {
data () {
return {
text: '來自子組件的數(shù)據(jù)'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</style>
demo:
尚未點擊“接受數(shù)據(jù)”按鈕前:

點擊接受數(shù)據(jù)按鈕后:

通過sync實現(xiàn)數(shù)據(jù)雙向綁定, 從而同步父子組件數(shù)據(jù)
通過以上三種方式, 我想你應(yīng)該能解決絕大多數(shù)父子組件通信的場景了,但讓我們再仔細考慮一下上面的通信場景,就會發(fā)現(xiàn)它們還可能存在的問題:
從子組件向父組件傳遞數(shù)據(jù)時,父子組件中的數(shù)據(jù)仍不是每時每刻都同步的
但在某些特殊的需求場景下,我們可能會希望父子組件中的數(shù)據(jù)時刻保持同步, 這時候你可能會像下面這樣做:
這是父組件中的template:
<son :foo="bar" v-on:update="val => bar = val"></son>
在子組件中, 我們通過props聲明的方式接收foo并使用
props: {
foo: [type]
}
同時每當(dāng)子組件中數(shù)據(jù)改變的時候,通過
this.$emit('update', newValue)
把參數(shù)newValue傳遞給父組件template中監(jiān)聽函數(shù)中的"val"。然后通過
val => bar = val
這個表達式就實現(xiàn)了bar = newValue. 這個時候,我們發(fā)現(xiàn)父組件中的關(guān)鍵數(shù)據(jù)bar被子組件改變(相等)了!
通過數(shù)據(jù)的雙向綁定, 父(組件)可以修改子的數(shù)據(jù), 子也可以修改父的數(shù)據(jù)
Vue提供了sync修飾符簡化上面的代碼,例如:
<comp :foo.sync="bar"></comp>
會被擴展為:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
然后你需要在子組件中改變父組件數(shù)據(jù)的時候, 需要觸發(fā)以下的自定義事件:
this.$emit("update:foo", newValue)
【注意】你可能覺得這好像和我上面提到的二中的“通過自定義事件(emit)從子組件向父組件中傳遞數(shù)據(jù)”的那一節(jié)的內(nèi)容似乎重疊了,。
然而并不是, 兩者有著父子組件關(guān)系上的不同, 下面我通過一行關(guān)鍵的代碼證明它們的區(qū)別所在
1.在我們講解sync的這一小節(jié)里, 自定義事件發(fā)生時候運行的響應(yīng)表達式是:
<son :foo="bar" v-on:update="val => bar = val"></son> 中的 "val => bar = val"
2.在二中的“通過自定義事件從子組件向父組件中傳遞數(shù)據(jù)” 里,自定義事件發(fā)生時候運行的響應(yīng)表達式是:
<Son v-on: eventYouDefined = "arg => functionYours(arg)" /> 中的 "arg => functionYours(arg)"
對前者, 表達式 val => bar = val意味著強制讓父組件的數(shù)據(jù)等于子組件傳遞過來的數(shù)據(jù), 這個時候,我們發(fā)現(xiàn)父子組件的地位是平等的。 父可以改變子(數(shù)據(jù)), 子也可以改變父(數(shù)據(jù))
對后者, 你的functionYours是在父組件中定義的, 在這個函數(shù)里, 你可以對從子組件接受來的arg數(shù)據(jù)做任意的操作或處理, 決定權(quán)完全落在父組件中, 也就是: 父可以改變子(數(shù)據(jù)), 但子不能直接改變父(數(shù)據(jù))!, 父中數(shù)據(jù)的變動只能由它自己決定
下面是一個展示demo:
父組件:
<template>
<div id="father">
<div>
我是父組件
<son
:wisdom.sync="wisdom"
:magic.sync="magic"
:attack.sync="attack"
:defense.sync="defense">
</son>
<p>智力: {{ wisdom }}</p>
<p>膜法: {{ magic }}</p>
<p>攻擊: {{ attack }}</p>
<p>防御: {{ defense }}</p>
</div>
</div>
</template>
<script>
import son from './son.vue'
export default {
data: function () {
return {
wisdom: 90,
magic: 160,
attack: 100,
defense: 80
}
},
components: {
son: son
}
}
</script>
<style scoped>
#father div {
padding: 10px;
margin: 10px;
border: 1px solid grey;
overflow: hidden;
}
</style>
子組件:
<template>
<div>
<p>我是子組件</p>
<p>智力: {{ wisdom }}</p>
<p>膜法: {{ magic }}</p>
<p>攻擊: {{ attack }}</p>
<p>防御: {{ defense }}</p>
<button @click="increment('wisdom')">增加智力</button>
<button @click="increment('magic')">增加膜法</button>
<button @click="increment('attack')">增加攻擊</button>
<button @click="increment('defense')">增加防御</button>
</div>
</template>
<script>
export default {
props: {
wisdom: Number,
magic: Number,
attack: Number,
defense: Number
},
methods: {
increment (dataName) {
let newValue = this[dataName] + 1
this.$emit(`update:${dataName}`, newValue)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</style>
點擊前

點擊增加子組件中“增加智力”按鈕的時候, 父組件和子組件中的智力參數(shù)同時從90變?yōu)?1

點擊增加子組件中“增加膜法”按鈕的時候, 父組件和子組件中的智力參數(shù)同時從160變?yōu)?61

數(shù)據(jù)雙向綁定是把雙刃劍
從好處上看:
1.它實現(xiàn)了父子組件數(shù)據(jù)的“實時”同步, 在某些數(shù)據(jù)場景下可能會使用到這一點
2.sync提供的語法糖使得雙向綁定的代碼變得很簡單
從壞處上看:
它破環(huán)了單向數(shù)據(jù)流的簡潔性, 這增加了分析數(shù)據(jù)時的難度
當(dāng)sync修飾的prop是個對象
我們對上面的例子修改一下, 把數(shù)據(jù)包裹在一個對象中傳遞下來:
父組件
<template>
<div id="father">
<div>
我是父組件
<son :analysisData.sync="analysisData">
</son>
<p>智力: {{ analysisData.wisdom }}</p>
<p>膜法: {{ analysisData.magic }}</p>
<p>攻擊: {{ analysisData.attack }}</p>
<p>防御: {{ analysisData.defense }}</p>
</div>
</div>
</template>
<script>
import son from './son.vue'
export default {
data: function () {
return {
analysisData: {
wisdom: 90,
magic: 160,
attack: 100,
defense: 80
}
}
},
components: {
son: son
}
}
</script>
<style scoped>
#father div {
padding: 10px;
margin: 10px;
border: 1px solid grey;
overflow: hidden;
}
</style>
子組件
<template>
<div>
<p>我是子組件</p>
<p>智力: {{ analysisData.wisdom }}</p>
<p>膜法: {{ analysisData.magic }}</p>
<p>攻擊: {{ analysisData.attack }}</p>
<p>防御: {{ analysisData.defense }}</p>
<button @click="increment('wisdom')">增加智力</button>
<button @click="increment('magic')">增加膜法</button>
<button @click="increment('attack')">增加攻擊</button>
<button @click="increment('defense')">增加防御</button>
</div>
</template>
<script>
export default {
props: {
analysisData: Object
},
methods: {
increment (dataName) {
let newObj = JSON.parse(JSON.stringify(this.analysisData))
newObj[dataName] += 1
this.$emit('update:analysisData', newObj)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</style>
demo同上
不要通過在子組件中修改引用類型props達到“父子組件數(shù)據(jù)同步”的需求!
父組件的數(shù)據(jù)傳遞給子組件, 一般通過props實現(xiàn), 而在實現(xiàn)“父子組件數(shù)據(jù)同步”這一需求的時候, 小伙伴們可能會發(fā)現(xiàn)一點: 在子組件中修改引用類型的props(如數(shù)組和對象)是可行的
1.不僅可以達到同時修改父組件中的數(shù)據(jù)(因為本來引用的就是同一個數(shù)據(jù))
2.而且還不會被Vue的檢測機制發(fā)現(xiàn)!(不會報錯)
但千萬不要這樣做, 這樣會讓數(shù)據(jù)流變得更加難以分析,如果你嘗試這樣做, 上面的做法可能會更好一些
不要這樣做,糟糕的做法:
父組件:
<template>
<div id="father">
<div>
我是父組件
<son :analysisData="analysisData">
</son>
<p>智力: {{ analysisData.wisdom }}</p>
<p>膜法: {{ analysisData.magic }}</p>
<p>攻擊: {{ analysisData.attack }}</p>
<p>防御: {{ analysisData.defense }}</p>
</div>
</div>
</template>
<script>
import son from './son.vue'
export default {
data: function () {
return {
analysisData: {
wisdom: 90,
magic: 160,
attack: 100,
defense: 80
}
}
},
components: {
son: son
}
}
</script>
<style scoped>
#father div {
padding: 10px;
margin: 10px;
border: 1px solid grey;
overflow: hidden;
}
</style>
子組件:
<template>
<div>
<p>我是子組件</p>
<p>智力: {{ analysisData.wisdom }}</p>
<p>膜法: {{ analysisData.magic }}</p>
<p>攻擊: {{ analysisData.attack }}</p>
<p>防御: {{ analysisData.defense }}</p>
<button @click="increment ('wisdom')">增加智力</button>
<button @click="increment ('magic')">增加膜法</button>
<button @click="increment ('attack')">增加攻擊</button>
<button @click="increment ('defense')">增加防御</button>
</div>
</template>
<script>
export default {
props: {
analysisData: Object
},
methods: {
increment (dataName) {
let obj = this.analysisData
obj[dataName] += 1
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button { float: left }
</style>
以上就是Vue中的父子組件通訊以及使用sync同步父子組件數(shù)據(jù)的詳細內(nèi)容,更多關(guān)于Vue的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡單了解Vue computed屬性及watch區(qū)別
這篇文章主要介紹了通過實例解析Vue computed屬性及watch區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
vue translate peoject實現(xiàn)在線翻譯功能【新手必看】
這篇文章主要介紹了vue translate peoject實現(xiàn)在線翻譯功能,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06
利用WebStorm創(chuàng)建一個Vue項目的完整步驟
WebStorm是一個非常適合學(xué)習(xí)和開發(fā)Vue項目的集成開發(fā)環(huán)境,下面這篇文章主要給大家介紹了關(guān)于利用WebStorm創(chuàng)建一個Vue項目的完整步驟,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2024-06-06
vue如何解決this.refs拿取v-for下元素undefine問題
這篇文章主要介紹了vue如何解決this.refs拿取v-for下元素undefine問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05

