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