上一篇文章我們提到Components可以接收父層傳遞過來的參數進行處理,這次我們首先就來看看子層通知父層的方法。
利用上一篇文章的最後一個範例來進行說明,當時我們使用兩個按鈕上面各有紀錄一個數量,透過使用者的點選讓該產品的數量開始遞減,用來解釋每個組件有自己獨立的Data,回憶完這個範例之後,我們期望對這個範例進行強化,除了原先的功能之外,我們希望可以在上面加一個數量累計的功能,當使用者點選按鈕的時候,去計算一個加總的數字,而總數這個Data應該就是隸屬於Parent內的,所以我們希望由子層來通知父層來更新這個數據。
為了完成這樣的範例,我們必須使用組件中的emit來完成,先來看看程式碼的部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
<style>
button {
padding: 10px;
}
h2 {
font-size: 18px;
font-family: "微軟正黑體";
}
</style>
</head>
<body>
<div id="app">
<h2>Order quantity : {{ state.total }}</h2>
<vuebutton title="iPhone" :start="5" @vuebutton-click="countTotal"></vuebutton>
<vuebutton title="iPad" :start="10" @vuebutton-click="countTotal"></vuebutton>
</div>
</body>
</html>
<script>
const { reactive } = Vue;
const app = {
setup(){
const state = reactive({
total: 0,
})
function countTotal(){
state.total ++;
}
return { state, countTotal }
}
}
const myVue = Vue.createApp(app);
myVue.component("vuebutton", {
props: {
title: {
type: String,
required: true,
},
start: {
type: Number,
default: 0,
},
},
template: `<button @click="minus">{{ state.title }} 庫存 {{ state.start }} 件</button>`,
setup(props, target){
const state = reactive({
title: props.title,
start: props.start,
})
function minus(){
if (state.start <= 0) {
state.start = 0;
} else {
state.start--;
target.emit("vuebutton-click");
}
}
return{ state, minus }
},
})
myVue.mount("#app");
</script>
上面案例的做法最終目的要達成的就像前面所說的,期望當按鈕按下之後,父層的total可以自動加1,而做法上除了在父層加上total這個data之外,還讓子層透過emit去“發射”一個事件出來,事件的名稱是vuebutton-click,當事件發射後,可以看到在父層使用v-on(簡寫成@)去監聽這個事件,當事情發生時會讓total+1。
注意第59行的setup中我們帶入了第二個參數target,在這邊就是我們的上層對象,但如果你認為以上的做法何必這麼麻煩,直接在vuebutton上面監聽click事件不就好了?
<vuebutton title="iPhone" :start="5" @click="state.total++"></vuebutton>
這樣的觀念就有點不正確了,因為這樣會讓total無條件+1,包含庫存數量已經歸0之後再按下這個按鈕,total的數量還是會不斷增加,也就是父層和子層的邏輯就被獨立開來了,這樣的做法沒辦法達到我們期望的效果。
從上面的範例我們可以得到結論: