---
# System prepended metadata

title: '2019/12/14 VueJS 教學筆記: 父子組件溝通'
tags: [emit, v-charts, VueJS]

---

---
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`來的好維護又有效率。

![](https://i.imgur.com/zGabuWD.png)

建立一個帶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`來搭建外觀，在這裡我們需要設置一些事件給外部調用

![](https://i.imgur.com/XJNFtIx.png)

```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，預期畫面會長這樣

![](https://i.imgur.com/AlAwfl4.png)

```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`