筆者今天自學vue組件間的傳值時非常混亂,故在此整理一下。
父子傳值
prop: 父向子傳值
步驟:
- 在子部件標籤中加入自定義屬性:
<Son :fatherMsg="msg"/>
- 在子部件中添加props數組:
props:['fatherMsg']
let Son={
props:['fatherMsg'],
template: `
<div>
<h2 class="display-4 text-secondary">This is Son</h2>
<p class="text-danger">{{fatherMsg}}</p>
</div>`
};
let Father = new Vue({
el:'#app',
data(){
return{
msg:'A message from father.'
}
},
components:{
Son
},
template:`
<div>
<h2 class="display-4 text-primary">This is Father</h2>
<Son :fatherMsg="msg"/>
</div>`
});
$emit:子向父傳值
子傳父與父傳子略有不同,依賴於事件,我們定義一個按鈕用於觸發事件。
步驟:
- 準備發送數據的方法
methods: {sendMsg(){this.$emit('sendMsgFromSon',this.msg);}},
- 準備一個觸發事件的對象並指定發送數據的方法(如button)
<button class="btn btn-success" @click="sendMsg">Send</button>
- 預留一個接收數據的參數
sonMsg: 'Message does not arrive.'
- 準備接收數據的方法並修改參數
methods: {getMsg(resp) {this.sonMsg = resp;}},
- 在子部件中加入自定義屬性接收數據並指定接收數據的方法
<Son @sendMsgFromSon="getMsg"/>
let Son = {
data() {
return {
msg: 'A message from son.'
}
},
methods: {
sendMsg() {
this.$emit('sendMsgFromSon', this.msg);
}
},
template: `
<div>
<h2 class="display-4 text-secondary">This is Son</h2>
<button class="btn btn-success" @click="sendMsg">Send</button>
</div>`,
};
let Father = new Vue({
el: '#app',
data() {
return {
sonMsg: 'Message does not arrive.'
}
},
components: {
Son
},
methods: {
getMsg(resp) {
this.sonMsg = resp;
}
},
template: `
<div>
<h2 class="display-4 text-primary">This is Father</h2>
<p class="text-danger">{{sonMsg}}</p>
<Son @sendMsgFromSon="getMsg"/>
</div>`
});
祖父子孫傳值
有了父子傳遞的經驗,那麼多層傳遞是否也可以用多個prop和emit實現呢?
當然可以,不過這顯得太麻煩了。於是,vue2.4推出了$attrs
和$listeners
幫我們解決了這個問題。
$attrs: 祖父向子孫傳值
不然發現,此處的Father和prop例子中的完全相同,Grandson也與Son除名字外完全相同,唯一的區別是多了中間組件。
關鍵步驟:
- 中間組件中添加子孫組件爲子組件
components:{Grandson},
- 使用
v-bind
爲子組件綁定$attrs
<Grandson v-bind="$attrs"/>
let Grandson = {
props: ['fatherMsg'],
template: `
<div>
<h2 class="display-4 text-success">This is Grandson</h2>
<p class="text-danger">{{fatherMsg}}</p>
</div>`
};
let Son = ({
components: {
Grandson
},
template: `
<div>
<h2 class="display-4 text-secondary">This is Son</h2>
<Grandson v-bind="$attrs"/>
</div>`
});
let Father = new Vue({
el: '#app',
data() {
return {
msg: 'A message from father.'
}
},
components: {
Son
},
template: `
<div>
<h2 class = "display-4 text-primary"> This is Father </h2>
<Son :fatherMsg="msg"/>
</div>`
});
$listeners: 子孫向祖父傳值
有了祖父傳子孫的經驗,子孫傳祖父也大同小異,只需在子傳父的基礎上添加即可:
- 爲中間組件添加子孫組件爲子組件
components:{Grandson},
- 使用
v-on
爲子組件綁定$listeners
<Grandson v-on="$listeners">
let Grandson = {
data() {
return {
msg: 'A message from Grandson.'
}
},
methods: {
sendMsg() {
this.$emit('sendMsgFromGrandson', this.msg);
}
},
template: `
<div>
<h2 class="display-4 text-success">This is Grandson</h2>
<button class="btn btn-success" @click="sendMsg">Send</button>
</div>`,
};
let Son = {
components: {
Grandson
},
template: `
<div>
<h2 class="display-4 text-secondary">This is Son</h2>
<Grandson v-on="$listeners"/>
</div>`
};
let Father = new Vue({
el: '#app',
data() {
return {
grandsonMsg: 'Message does not arrive.'
}
},
components: {
Son
},
methods: {
getMsg(resp) {
this.grandsonMsg = resp;
}
},
template: `
<div>
<h2 class="display-4 text-primary">This is Father</h2>
<p class="text-danger">{{grandsonMsg}}</p>
<Son @sendMsgFromGrandson="getMsg"/>
</div>`
});
任意傳值
中央事件總線:任意組件間傳值
最後的方法叫做中央事件總線,它可以實現任意組件的傳值,無論是父子、祖父子孫、還是兄弟。
這裏我們創建了一個全局的bus,這個bus就像一個接線員,接收一個數據,再發送給別的組件。
步驟:
- 聲明一個全局的Vue作爲總線
let bus = new Vue();
- 爲發送數據的組件添加方法,使用
bus.$emit
發送
methods:{sendMsg(){bus.$emit('msgFromComponent1', this.msg);}},
- 爲發送數據的組件提供事件觸發方法(此處使用按鈕):
<button class="btn btn-success" @click="sendMsg">Send</button>
- 爲接收的數據預留槽位
data(){return{msg: 'Message does not arrive.'}},
- 爲接收數據的組件提供方法,使用
bus.$on
接收(此處使用mounted
,意味着數據改變就調用方法,從而實現同步更新)
mounted() {bus.$on('msgFromComponent1', (resp) => {this.msg = resp;})},
let bus = new Vue();
let Component1 = new Vue({
el: '#app',
data() {
return {
msg: 'A message from component1.'
}
},
methods: {
sendMsg() {
bus.$emit('msgFromComponent1', this.msg);
}
},
template: `
<div>
<h2 class="display-4 text-primary">This is Component1</h2>
<button class="btn btn-success" @click="sendMsg">Send</button>
</div>`
});
let Component2 = new Vue({
el: '#app2',
data() {
return {
msg: 'Message does not arrive.'
}
},
mounted() {
bus.$on('msgFromComponent1', (resp) => {
this.msg = resp;
})
},
template: `
<div>
<h2 class="display-4 text-secondary">This is Component2</h2>
<p class="text-danger">{{msg}}</p>
</div>`
})
以上5種方法講解完成,除此之外,還有provided
/ inject
和$parent
/ $children[index]
等方法,不過不太常用,大家可以自行了解。