總結Vue組件的通訊

寫在前面前端

總結是一種學習方式,取長補短是一種學習態度。 全文總結了6種Vue組件間的通訊方式,若有誤區,歡迎指正!vue

一、props/$emit

最經常使用的一種父子間傳遞數據的方式。vuex

描述:數組

父組件經過綁定屬性來向子組件傳遞數據,子組件經過 props 屬性來獲取對應的數據;子組件經過 $emit 事件向父組件發送消息,將本身的數據傳遞給父組件。瀏覽器

使用方法:安全

// 父組件compA
<template>
    <div>
        <compB
        :title="value"
        :moreBtn="moreBtn"
        @more="onMore"/>
    </div>
    
</template>
<script>
import compB from './compB'
export default{
    name: 'compA',
    components:{
        compB
    },
    data(){
        return {
            value: '',
            moreBtn: true
        }
    },
    method:{
        onMore(value){
            console.log('value', value)
            //點擊查看更多按鈕
        }
    }
}
</script>
複製代碼
//子組件compB
<template>
    <div>
        <div class="title">{{value}}</div>
        <div v-if="moreBtn" @click="handleMore">查看更多</div>
    </div>
</template>
<script>
export default{
    name: 'compB',
    data(){
        return {
        }
    },
    props: {
        title: {
            type: String,
            default: '標題'
        },
        moreBtn: {
            type: Boolean,
            default: false
        }
    }
    method:{
        handleMore(){
            this.$emit('more', '點擊查看更多按鈕')
        }
    }
}
</script>
複製代碼

props使得父子之間造成一種單向數據流,父元素更新的時候,子元素的狀態也會隨之改變。但反之會致使你的應用的數據流向難以理解。bash

注意在 JavaScript 中對象和數組是經過引用傳入的,因此對於一個數組或對象類型的 prop 來講,在子組件中改變這個對象或數組自己將會影響到父組件的狀態。app

若是經過prop傳遞的數據是數組或者是對象,則是能夠改變父對象的值,可是不推薦。若是傳遞的是個字符串,還要改變父組件的值,那麼Vue 會在瀏覽器的控制檯中發出警告。異步

在某些狀況下,須要子組件改變父組件的值,推薦使用(2.3.0+)版本新增的.sync語法糖。async

使用方法:

// A.vue
<template>
    <add-modal 
      v-if="modalVisiable" 
      :visiable.sync='modalVisiable' 
      @submit="saveForm"
    />
</template>
<script>
export default {
    name: 'A',
    data(){
        return {
            modalVisiable: false
        }
    }
}
</script>
複製代碼
// B.vue
<template>
    <Modal 
      v-model="show"
      width="600"
      title="彈框" 
      :loading='true'
      @on-cancel="$emit('update:visiable', false)"
    >
    </Modal>
</template>
<script>
export default {
    name: 'A',
    data(){
        return {
            show: false,
        }
    },
    created(){
        this.show = this.visiable
    },
    props: {
        visiable: {
          type: Boolean,
          default: false
        }
    },
}
</script>
複製代碼

經過 this.$emit('update:visiable', false) 來改變父元素的狀態

二、$emit/$on

eventBus事件總線

描述:

這個方法是經過建立一個空的 vue 實例,當作 $emit 事件的處理中心(事件總線),經過他來觸發以及監聽事件,方便的實現了任意組件間的通訊,包含父子,兄弟,隔代組件。

如下圖爲例,咱們要實現的是A組件和C組件向B組件傳輸數據。

使用方法:

<!--home.vue-->
<template>
  <div>
    <div>我是父元素,個人num的值是:{{num}}</div>
    <a-comp :event="bus" :num.sync='num'></a-comp>
    <c-comp :event="bus" :num='num'></c-comp>
    <b-comp :event="bus"></b-comp>
  </div>
</template>
<script>
import Vue from 'vue';
import aComp from './a.vue';
import bComp from './b.vue';
import cComp from './c.vue';
// 建立一個空的vue實例
const bus = new Vue();
export default {
  'name': 'example',
  data() {
    return {
      bus,
      'num': 1
    };
  },
  'components': {
    aComp,
    bComp,
    cComp
  }
};
</script>
複製代碼
<!--a.vue-->
<template>
  <div class="a-content">
    我是組件A,
    <el-button type="primary" @click="send">點擊我向B發送東西</el-button>
  </div>
</template>
<script>
export default {
  'name': 'aComp',
  'props': [
    'event',
    'num'
  ],
  data() {
    return {'nb': 0};
  },
  created() {
    this.nb = this.num;
  },
  'methods': {
    send() {
      this.nb = this.nb + 1;
      this.$emit('update:num', this.nb);
      // 經過$emit 來觸發phone-a事件
      this.event.$emit('phone-a', '我是組件A啊', this.nb);
    }
  }
};
</script>
複製代碼
<!--c.vue-->
<template>
  <div>
    我是組件C,
    <el-button type="primary" @click="send">點擊我向B發送東西</el-button>
  </div>
</template>
<script>
export default {
  'name': 'cComp',
  'props': [
    'event',
    'num'
  ],
  data() {
    return {};
  },
  'methods': {
    send() {
      console.log(this.num);
      this.event.$emit('phone-c', `我是組件C啊${this.num}`);
    }
  }
};
</script>
複製代碼
<!--b.vue-->
<template>
  <div class="b-content">
    <div>我是組件B,我會接收組件A及組件C</div>
    <div>
      A: {{a}}
    </div>
    <div>
      B: {{c}}
    </div>
  </div>
</template>
<script>
export default {
  'name': 'bComp',
  data() {
    return {
      'a': '',
      'c': ''
    };
  },
  'props': ['event'],
  mounted() {
    // 經過$on來監聽 phone-a 、phone-c 事件
    this.event.$on('phone-a', (a, num) => {
      this.a = `${a},我在改變父元素傳過來的值num: ${num}`;
    });
    this.event.$on('phone-c', c => {
      this.c = c;
    });
  }
};
</script>
複製代碼

其中最重要的是他們必需要在公共的實例中才能實現數據的互相傳遞。

三、provide / inject

描述:

這對選項須要一塊兒使用,父組件使用 provide 向下提供數據,其下全部子組件均可以經過inject注入。無論中間隔了多少代,均可以注入多個來自不一樣父級提供的數據

  • provide 選項是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性
  • inject 選項是一個字符串數組,或一個對象

使用方法:

// 父組件
<template>
  <div>
    <com-a></com-a>
  </div>
</template>
<script>
import ComA from './a';

export default {
  'name': 'home',
  'components': {ComA},
  provide() {
    return {
      'a': 'Hello',
      'show': val => !val
    };
  }
};
</script>

複製代碼
// 子組件
<template>
  <div>
    <el-button @click="showFn(textShow)">點擊我切換下面文字展現及隱藏</el-button>
    <div v-if="textShow">我是一段隨意被操控的文字{{a}}</div>
  </div>
</template>
<script>
export default {
  'name': 'ComA',
  data() {
    return {'textShow': true};
  },
  'inject': [
    'a',
    'show'
  ],
  'methods': {
    showFn(val) {
      this.textShow = this.show(val);
    }
  }
};
</script>
複製代碼

若是是在app.vue 文件(根文件)裏面全局註冊信息,就能夠在整個路由裏面去引用 (相似於全局的數據管理vuex)

<template>
    <div>
        <router-view></router-view>
    </div>
</template>
<script>
export default {
    name: 'app',
    provide(){
        return {
            app: this
        }
    }
}
</script>
複製代碼

接下來任何組件只要經過inject注入app的話,均可以直接經過this.app.xx 來訪問app.vue 的全部實例。

四、$parent / $children

描述:

$parent 能夠用來從一個子組件訪問父組件的實例。它提供了一種機會,能夠在後期隨時觸達父級組件,來替代將數據以 prop 的方式傳入子組件的方式

<template>
  <div class="b-content">
    <div>我是子組件</div>
    <span>{{msgText}}</span>
  </div>
</template>
<script>
export default {
  'name': 'childComp',
  data() {
    return {
      'msgText': '',
      'childMsg': '來自子元素的吶喊'
    };
  },
  created() {
    this.msgText = this.$parent.parentMsg;
    // MsgText: 來自父組件的呵呵
  }
};
</script>
複製代碼

$children能夠遍歷所有子組件,須要注意 $children 並不保證順序,也不是響應式的。

<template>
  <div class="b-content">
    <div>我是父組件</div>
    <child-comp></child-comp>
  </div>
</template>
<script>
import childComp from './child';

export default {
  'name': 'parentComp',
  data() {
    return {'parentMsg': '來自父組件的呵呵'};
  },
  'components': {childComp},
  mounted() {
    // 讀取子組件數據,注意$children子組件的排序是不安全的
    console.log(this.$children[0].childMsg);
    // 來自子元素的吶喊
  }
};
</script>
複製代碼

五、$root & refs

描述:

$root 屬性是在每一個 new Vue 實例的子組件中,其根實例能夠經過 $root 屬性進行訪問。例如,在這個根實例中:

// Vue 根實例
new Vue({
  data: {
    foo: 1
  },
  computed: {
    bar: function () { /* ... */ }
  },
  methods: {
    baz: function () { /* ... */ }
  }
})
複製代碼

全部的子組件均可以將這個實例做爲一個全局 store 來訪問或使用。

// 獲取根組件的數據
this.$root.foo

// 寫入根組件的數據
this.$root.foo = 2

// 訪問根組件的計算屬性
this.$root.bar

// 調用根組件的方法
this.$root.baz()
複製代碼

$refs屬性當你須要在 JavaScript 裏直接訪問一個子組件。你能夠經過 ref 這個 attribute 爲子組件賦予一個 ID 引用。 一個對象,持有註冊過 ref 特性 的全部 DOM 元素和組件實例。例如:

<my-component ref="childrenCompA"></my-component>
複製代碼
訪問子組件:this.$refs.childrenCompA
複製代碼

六、Vuex

在作中大型的單頁應用的時候,例如須要多人協做開發,全局維護登陸狀態等,咱們能夠選擇vuex來進行狀態管理。

state裏面是存儲在vuex裏的數據,經過在根實例中註冊 store 選項,該 store 實例會注入到根組件下的全部子組件中,且子組件能經過 this.$store 訪問到。

當須要獲取多個數據時,能夠經過下面這種方式:

import {mapState} from 'vuex'
computed:{
    ...mapState('commissionSetting', [
      'listData'
    ])
},
複製代碼

不是全部的狀態都適合放在vuex裏面,有些狀態只屬於單個組件,因此仍是要視狀況來定。

mutations 更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation

const mutations = {
  updateListData(state, payload){
    state.listData = payload
  }
}
複製代碼

不能直接調用updateListData,須要以相應的 type 調用 store.commit 方法:

store.commit('updateListData', data)
複製代碼

actions 提交的是 mutation,而不是直接變動狀態。 Action 能夠包含任意異步操做。

寫一個簡單的action:

async getListAction({commit}, params){
    const result = await getList(params)
    const {code, message, data} = result
    if(code === SUCCESS && data){
      // 提交更改數據
      commit('updateListData', data.rows)
    }else{
      vueInit.$Message.error({
        content: message || '請您稍後再試~'
      });
    }
    return result
  },
複製代碼

Vuex 並不限制你的代碼結構。可是,它規定了一些須要遵照的規則:

  • 應用層級的狀態應該集中到單個 store 對象中。
  • 提交 mutation 是更改狀態的惟一方法,而且這個過程是同步的。
  • 異步邏輯都應該封裝到 action 裏面。

只要你遵照以上規則,如何組織代碼隨你便。若是你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。

最後

生命不息,學習不止。新的知識點層出不窮,對於我們開發者來講,最重要的是學會而且能應用到實際項目中。

關注咱們

關注公衆號前端論道
相關文章
相關標籤/搜索