---
title: '2019/12/14 VueJS 教學筆記: 父子組件溝通'
disqus: hackmd
---
2019/12/14 VueJS 教學筆記: 父子組件溝通
===
綱要
[TOC]
組件之間溝通的方式
---
除了上回上課使用到的Vuex之外,兩個組件之間需要交換事件與資料時,父組件可以用`Props`傳遞給子組件、而子組件可以用`Event emitter`傳遞給父組件。
但如果你需要的是「同層級組件」之間的資料傳遞,建議使用[Vuex](https://hackmd.io/@FortesHuang/Byb98SvTB)會比VueJS 1.x版本時常見的`Event bus`來的好維護又有效率。

建立一個帶TABS與表頭文字的子組件來顯示v-charts圖表
---
在這個範例當中,我們要做一個TABS頁籤的子組件,以不同的參ㄒ數顯示不同圖表內容以及表頭文字
由於`v-charts`是以`echart`為基礎開發出來的,所以需先安裝`echart`再來安裝`v-charts`:
`npm i echart --save`
`npm i v-charts --save`
在`main.js`中以全域的方式調用
```javascript=
import Vue from 'vue'
import App from './App.vue'
...
...
import VCharts from 'v-charts'
Vue.use(VCharts)
```
新增一個子組件`tabs.vue`
**tabs.vue**
[Github範例](https://github.com/fortes1219/vue_0803/blob/0803/src/components/tabs.vue)
tabs使用的是`elementUI`的`radio-button`來搭建外觀,在這裡我們需要設置一些事件給外部調用

```htmlmixed=
<template>
<div>
<!--Duration-->
<!--這裡的v-if都是稍候外部父組件調用時設置的props,比如 :durationDisplay="true"-->
<div v-if="durationsDisplay" key="duration" class="row vertical">
<el-radio-group
v-model="duration.select"
@change="durationChange"
>
<el-radio-button
v-for="(item, index) in duration.items"
:key="index"
:label="item.label"
>{{ item.name }}</el-radio-button>
</el-radio-group>
<h2 data-space="space-vertical">{{ headerText }}</h2>
</div>
<!--On sale-->
<!--如果外層父組件設置 :onsaleDisplay="true",這裡的內容就會顯示-->
<div v-if="onSaleDisplay" key="onSale" class="row vertical">
<el-radio-group
v-if="onSaleDisplay"
v-model="onSale.select"
@change="onSaleChange"
>
<el-radio-button
v-for="(item, index) in onSale.items"
:key="index"
:label="item.label"
>{{ item.name }}</el-radio-button>
</el-radio-group>
<br>
</div>
</div>
</template>
```
```javascript=
<script>
export default {
name: "tabs", // 調用時的名稱必須一致,ex: import tabs from '@/path/tabs'
props: {
// 定義props以及型別、預設值
durationsDisplay: {
type: Boolean,
default: false
},
onSaleDisplay: {
type: Boolean,
default: false
}
},
data() {
return {
duration: {
select: 0,
items: [
{ label: 0, name: "有效總收益" },
{ label: 1, name: "獲利率" },
]
},
onSale: {
select: 0,
items: [
{ label: 0, name: "推薦" },
{ label: 1, name: "人氣" },
{ label: 2, name: "最新" }
]
}
}
},
computed: {
headerText() {
let text = ''
switch (this.duration.select) {
case 0:
text = '有效總收益'
break
case 1:
text = '獲利率'
break
}
return text
}
},
methods: {
durationChange(val) {
this.duration.select = val
// 利用 this.$emit將所得到的資料傳出去
this.$emit("tabDuration", val)
},
onSaleChange(val) {
this.onSale.select = val
this.$emit("tabOnSale", val)
}
},
created() {
// 因為是TABS,拆到外頭作子組件必須要有一個預設被按下的行為先觸發,光只有Data的資料並不代表已經被按過。
this.durationChange(this.duration.select)
this.onSaleChange(this.onSale.select)
}
}
</script>
```
**Component.vue**
[Github範例](https://github.com/fortes1219/vue_0803/blob/0803/src/components/home/Component.vue)
到建立好的頁面來使用剛才新增過的tabs,預期畫面會長這樣

```htmlmixed=
<template>
<div class="page component">
<h1>父子組件資料傳遞</h1>
<br />
<!-- v-on:tabDuration就是在tabs.vue子組件中,durationChange()透過this.$emit("tabDuration", val)中,tabDuration的鏡射對象 -->
<tabs
:durationsDisplay="true"
@tabDuration="tabDuration"
/>
<!-- <tabs :onSaleDisplay="true" @tabOnSale="tabOnSale"></tabs> -->
<!--注意,當你有兩種相似的data結構時一定要加上key防止重新render時沒有更新畫面-->
<ve-line
v-if="currentTab == 0"
key="dayTabs01"
:data="income"
:loading="true"
:settings="lineChartConfig"
:legend-visible="false"
:colors="lineChartColor.blue"
/>
<ve-line
v-if="currentTab == 1"
key="dayTabs02"
:data="incomeRate"
:loading="true"
:settings="lineChartConfigPercentage"
:legend-visible="false"
:colors="lineChartColor.orange"
/>
</div>
</template>
```
```javascript=
<script>
import tabs from "../tabs" // 子組件中 name: "tabs" 的作用就在這
export default {
components: {
tabs // 註冊組件
},
data() {
this.lineChartConfig = {
area: true
},
this.lineChartConfigPercentage = {
area: true,
yAxisType: ['percent'] // 當資料型態需要使用百分比時按照v-charts的option來調用
}
return {
currentTab: "",
lineChartColor: {
blue: ["#dcb5ff"],
orange: ["#a5a552"],
green: ["#ffe153"]
},
income: {
columns: ["日期", "有效總收益"],
rows: [
{ "日期": "08/17", "有效總收益": "100000" },
{ "日期": "08/18", "有效總收益": "350000" },
{ "日期": "08/19", "有效總收益": "490000" },
{ "日期": "08/20", "有效總收益": "370000" },
{ "日期": "08/21", "有效總收益": "540000" },
{ "日期": "08/22", "有效總收益": "350000" },
{ "日期": "08/23", "有效總收益": "650000" },
{ "日期": "08/24", "有效總收益": "1000000" },
{ "日期": "08/25", "有效總收益": "1400000" },
{ "日期": "08/26", "有效總收益": "700000" }
]
},
incomeRate: {
columns: ['日期', '獲利率'],
rows: [
{ "日期": '08/17', '獲利率': '-0.05' },
{ "日期": '08/18', '獲利率': '0.03' },
{ "日期": '08/19', '獲利率': '0.05' },
{ "日期": '08/20', '獲利率': '0.03' },
{ "日期": '08/21', '獲利率': '0.06' },
{ "日期": '08/22', '獲利率': '0.025' },
{ "日期": '08/23', '獲利率': '0.06' },
{ "日期": '08/24', '獲利率': '0.1' },
{ "日期": '08/25', '獲利率': '0.13' },
{ "日期": '08/26', '獲利率': '0.0865' }
]
}
}
},
methods: {
tabDuration(val) {
this.currentTab = val
console.log("duration type: ", val)
},
tabOnSale(val) {
this.currentTab = val
console.log("on sale type: ", val)
}
},
created() {}
}
</script>
```
###### tags: `VueJS` `emit` `v-charts`