Day 23:「儿子,这是你的零用钱」- 元件间的资料传递

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 24】JavaScript 事件处理

>>:  列表与 Key ( Day 10 )

[13th][Day27] cluster

在丛集管理方面,kubernetes 将 cluster 中的机器划分为一个 master 节点以及...

变更管理和变更控制(Change Management and Change Control)

什麽是变化,我们在变化什麽? 当涉及到更改时,至关重要的是定义什麽是更改并阐明我们正在更改的内容。...

day21 : TIDB on K8S (上)

对大部分使用k8s服务的人来说都会有一个探讨的问题是,到底DB是否适合上k8s,其实我个人是觉得不适...

【修正模型】4-3 事件循环(Event Loop)与任务队列(Job Queue)

同步(Synchronous)与非同步(Asynchronous) 在理解执行上下文与呼叫堆叠之後,...

Day-01 前言

这是我30天的自我挑战,每篇文章都记录着自己的成长与收获,也请大家多多指教! Android是甚麽...