兔大夫:
「請問是兔豪的家屬嗎?」
兔豪爸:
「是,我就是。 請問我鵝子他...」
兔大夫:
「抱歉,我盡力了 ...」
「他得的是一種 "會常常跟你討零用錢而且不管給他多少錢他都一定會花到只剩 50 塊然後會把這 50 塊還你" 症。」
「這是不治之症,目前醫學上還沒有辦法改善。」
兔豪爸:
「所以,難道說我的鵝子...」
兔大夫:
「對,沒救了。」
兔豪:
「爸,我要零用錢!」
兔豪爸:
「竟然已經 ... 沒救了。」
兔豪:
「爸,我要零用錢!」
兔豪爸:
「好 ... 好... 你等等我。」
看來,就是有一個土豪爸,才會有個土豪兒子呀。
有多少花多少,但還會留個 50 元也算良心了 XD
不過其實啊,
像兔豪爸和兔豪這樣的行為,
就是很經典的父子元件間的資料傳遞!
就讓我們來看看怎麼實現吧!
其實啊,在實現元件間的資料傳遞時,
父傳子是最簡單的!
就是運用在元素上綁定屬性就可以了!
來看看簡單的舉例:
<template>
<RabbitSon :allowance="money" />
</template>
<script>
export default {
name: "兔豪爸",
data() {
return {
money: 1000,
}
}
}
</script>
這樣代表著兔豪爸已經把 1000 給兔豪了。
不過有一點可以注意的是,
在這個範例中的 :allowance
是一個自訂屬性。
而其實不管屬性是否是自訂的,
只要在子元件的 props 有對應的名稱,
就能收到來自父元件的資料。
「欸?就這麼輕鬆?」
沒錯哦,就是這麼輕鬆。
不過這只是傳遞過去的部分,
其實啊,即使我們給了兔豪,
兔豪也還要做收下的動作。
所以我們接著就來看看兔豪要怎麼收到爸爸給的零用錢囉!
前面也說過,props
必須要與父元件傳遞資料給我們時的屬性名稱對應,
這是什麼意思?
因為在子元件裡,
比需要像在 data( )
中那樣定義會用到什麼變數,
在 props
中也一樣。
我們必須列出會要收到資料的屬性名稱,
這樣就會自動與元素上的屬性對應到了。
直接看例子:
<!-- RabbitSon.vue -->
<template>
<div>
我是兔豪,<br />
爸爸給了我 {{allowance}} 塊當用錢。
</div>
</template>
<script>
export default {
name: "兔豪",
props: ["allowance"]
}
</script>
像這樣,從父元件傳入時的屬性叫做 allowance
,那 props 中也必須定義出 "allowance"
才能順利收取到資料。
不過這樣還是沒有還原出兔豪把「50 塊還給爸爸」的動作。 接下來就是要講解如何實現從子元件傳遞資料回父元件。
想要將資料傳遞回父元件,就必須靠 emit
。
emit 就是發射、發出的意思,
也就是指我們把資料由低往高送出的這個行為。
我們先直接看範例,
兔豪要在一收到錢時就馬上花掉,
我們就在子元件掛載時處理這件事情。
也就會是:
<!-- RabbitSon.vue -->
<template>
<div>
我是兔豪,<br />
爸爸給了我 {{allowance}} 塊當用錢。
</div>
</template>
<script>
export default {
name: "兔豪",
props: ["allowance"],
mounted() {
console.log("收到零用錢 " + this.allowance + " 元")
this.$emit("return", 50)
}
}
</script>
是不是看不懂那行 $emit 在做些什麼?
沒關係,直接來看一下 emit 的用法:
$emit( 事件名稱, 傳遞內容 )
這邊所指的事件名稱就是傳遞給父元件時,
在父元件那邊所發生的事件。
所以跟父元件傳遞資料進來同理,
我們傳遞出去時父元件也須接收。
來看一下範例:
<template>
<RabbitSon :allowance="money" @return="getMoney($event)" />
</template>
<script>
export default {
name: "兔豪爸",
data() {
return {
money: 1000,
}
},
methods: {
getMoney( money ) {
console.log("兒子兔豪花剩下的 " + money + " 元")
}
}
}
</script>
會發現子元件身上有一個事件 @return
,這個事件就是在子元件中 this.$emit("return", 50)
時的事件名稱
"return"。
在子元件內執行 emit 時,父元件這邊的子元件就會觸發 emit 時設定的事件。
而 this.$emit("return", 50)
後面的傳遞內容 "50" 就是事件中可以獲取的事件參數
event。
這可能太複雜了,
這樣很容易聽不懂,
我們來圖解。
這樣應該就比較看得出互相傳遞的關係了!
不過現在我們仍然只是傳遞很簡單的純值內容,如果要傳遞物件
呢? 那我們就要講到物件解構
的特性啦!
如果只是傳遞純值,這很簡單沒有問題。
但如果是傳遞物件就必須要小心了!
直覺,我們可能會這樣傳遞:
<template>
<RabbitSon :allowance="wallet" @return="getMoney($event)" />
</template>
<script>
export default {
name: "兔豪爸",
data() {
return {
wallet: {
money: 1000,
msg: "省著點用",
},
}
},
methods: {
getMoney( money ) {
console.log("兒子兔豪花剩下的 " + money + " 元")
}
}
}
</script>
但這會有一個問題,就是不安全
。
我們直接把物件當作屬性來傳遞時,因為傳遞的是整個物件,所以父元件傳遞的和子元件所拿到的物件是同個記憶體位置
,這樣在子元件中修改 props 進來的資料時,父元件的資料也會被同步改掉。
「兔兔,這樣不是超讚的嘛! 要往回傳遞的時候都不用寫那個很難搞懂的 emit,現在直接修改內容就好了!」
不,其實這一點都不讚哦!
為什麼前面說這麼做不安全呢?
因為這樣其實是子元件對父元件資料汙染
了!
之前有說過 SFC 元件的好處就是邏輯清晰,
如果我現在在內部處理資料的時候就會不小心改到父層的資料,這不是太可怕了嗎?!
所以我們必須要用物件解構
的方式來進行!
如果傳遞的是整個物件,
我們就用直接的 v-bind
語法來完成:
<template>
<RabbitSon v-bind="wallet" @return="getMoney($event)" />
</template>
<script>
export default {
name: "兔豪爸",
data() {
return {
wallet: {
money: 1000,
msg: "省著點用",
},
}
},
methods: {
getMoney( money ) {
console.log("兒子兔豪花剩下的 " + money + " 元")
}
}
}
</script>
那在子元件中則是要修改成這樣:
<!-- RabbitSon.vue -->
<template>
<div>
我是兔豪,<br />
爸爸給了我 {{money}} 塊當用錢。<br />
爸爸還說:「{{msg}}」
</div>
</template>
<script>
export default {
name: "兔豪",
props: ["money","msg"],
mounted() {
console.log("收到零用錢 " + this.money + " 元")
this.$emit("return", 50)
}
}
</script>
可以發現雖然我們父元件傳送的是物件,但在子元件中完全看不到物件的蹤影,倒是 props 裡都是物件內的屬性。
沒錯,透過物件解構我們可以把屬性拆出來都送給子元件
,這樣就可以避免掉資料污染的問題。
一樣再來上個圖解。
先是以整個物件的方式傳遞,
到了子元件後再解開。
這樣就可以了!
關於元件資料傳遞就差不多介紹到這裡啦!
emit 的部分可能比較難理解一些,
不過搭配圖解和前面敘述,
應該多看幾次就能明白了~
下一篇,
又要來介紹很有趣的東西了!
好期待~~
關於兔兔們:
( # 兔兔小聲說 )
許多人認為,夏威夷披薩是邪教。
認識兔兔的朋友們都知道兔兔不太吃夏威夷披薩,但絕對不是因為它是邪教,是因為朋友們都認為它是邪教所以我不敢吃啊 XD
不過其實有趣的是,
你們知道夏威夷披薩跟夏威夷關係其實並不大嗎?對,我也驚呆了。
轉述一下維基百科的內容:
「夏威夷披薩是希臘出生的加拿大廚師發明的,因為
受到中華料理中的酸、甜元素啟發
,所以做出了夏威夷披薩。而披薩叫做夏威夷,只是因為使用的鳳梨罐頭上面印著夏威夷字樣
。」好啦,不過鳳梨的確是夏威夷特產,當地也有酸甜氣味的早餐料理叫做 Acai bowl,有到夏威夷可以吃吃看哦~
請問兔兔大師有其他教學文章嗎
覺得您寫的文章特別好懂 尤其是這邊很難的父子互傳的特性 在這邊整個茅塞頓開!!!
之前在某大師的008書籍 還是蠻模糊
期待有JS或其他前端的教學文,有出書一定支持,插圖都太美了哈哈