iT邦幫忙

2021 iThome 鐵人賽

DAY 23
2

Day23-Banner

兔大夫:
「請問是兔豪的家屬嗎?」

兔豪爸:
「是,我就是。 請問我鵝子他...」

兔大夫:
「抱歉,我盡力了 ...」

「他得的是一種 "會常常跟你討零用錢而且不管給他多少錢他都一定會花到只剩 50 塊然後會把這 50 塊還你" 症。」

「這是不治之症,目前醫學上還沒有辦法改善。」

兔豪爸:
「所以,難道說我的鵝子...」

兔大夫:
「對,沒救了。」

 
 
兔豪:
「爸,我要零用錢!」
 

carrotPoint 沒救了

兔豪爸:
「竟然已經 ... 沒救了。」

兔豪:
「爸,我要零用錢!」

兔豪爸:
「好 ... 好... 你等等我。」

 
看來,就是有一個土豪爸,才會有個土豪兒子呀。

有多少花多少,但還會留個 50 元也算良心了 XD

不過其實啊,
像兔豪爸和兔豪這樣的行為,
就是很經典的父子元件間的資料傳遞!

就讓我們來看看怎麼實現吧!
 

carrotPoint 有去無回

其實啊,在實現元件間的資料傳遞時,
父傳子是最簡單的!

就是運用在元素上綁定屬性就可以了!

來看看簡單的舉例:

<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 塊還給爸爸」的動作。 接下來就是要講解如何實現從子元件傳遞資料回父元件。
 

carrotPoint 爸,還你

想要將資料傳遞回父元件,就必須靠 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。

這可能太複雜了,
這樣很容易聽不懂,

我們來圖解。

這樣應該就比較看得出互相傳遞的關係了!

  • 由父元件把資料綁定在子元件 props 來傳給子元件
  • 由子元件 emit 出去到父元件發出事件讓父元件接收處理

不過現在我們仍然只是傳遞很簡單的純值內容,如果要傳遞物件呢? 那我們就要講到物件解構的特性啦!
 

carrotPoint 傳遞物件

如果只是傳遞純值,這很簡單沒有問題。

但如果是傳遞物件就必須要小心了!

直覺,我們可能會這樣傳遞:

<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 的部分可能比較難理解一些,
不過搭配圖解和前面敘述,
應該多看幾次就能明白了~

下一篇,
又要來介紹很有趣的東西了!

好期待~~
 

carrotPoint 給你們的回家作業:

  • 作業實施要點:
    • 看懂今天的東西!
       

關於兔兔們:


 


( # 兔兔小聲說 )

許多人認為,夏威夷披薩是邪教。

認識兔兔的朋友們都知道兔兔不太吃夏威夷披薩,但絕對不是因為它是邪教,是因為朋友們都認為它是邪教所以我不敢吃啊 XD

不過其實有趣的是,
你們知道夏威夷披薩跟夏威夷關係其實並不大嗎?

對,我也驚呆了。

轉述一下維基百科的內容:

「夏威夷披薩是希臘出生的加拿大廚師發明的,因為受到中華料理中的酸、甜元素啟發,所以做出了夏威夷披薩。而披薩叫做夏威夷,只是因為使用的鳳梨罐頭上面印著夏威夷字樣。」

好啦,不過鳳梨的確是夏威夷特產,當地也有酸甜氣味的早餐料理叫做 Acai bowl,有到夏威夷可以吃吃看哦~


上一篇
Day 22:「您好,歡迎登入 Vuta 奇幻世界」- 事件處理
下一篇
Day 24:「Switch 也要換遊戲片啦~」- Slot 插槽
系列文
排版神器 Tailwind CSS~和兔兔一起快速上手漂亮的元件開發!32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
碼農
iT邦新手 4 級 ‧ 2023-02-03 11:53:30

請問兔兔大師有其他教學文章嗎

覺得您寫的文章特別好懂 尤其是這邊很難的父子互傳的特性 在這邊整個茅塞頓開!!!
之前在某大師的008書籍 還是蠻模糊

期待有JS或其他前端的教學文,有出書一定支持,插圖都太美了哈哈

我要留言

立即登入留言