vue2.0開發聊天程序(三)組件的通訊

天地不仁以萬物爲芻狗,宇宙無義視衆生如螻蟻
                  ——蕭鼎和我html

上一節列出了5個關鍵點,第一個路由已經解決了,接下來解決第二個問題:vue

組件的通訊問題web

1、組件的關係vue-router

組件之間的關係無非就是兩種父子關係沒有父子關係。爲何我這樣說呢?
按道理應該還有兄弟關係(也就是並列的組件,好比一個組件中引用了hreder和footer組件。),還有爺孫關係(好比我有七個Calabash Brothers組件,放在的HanHan組件下,而HanHan組件放在了Chairman Mao組件下)
那麼不該該是父子、爺孫、兄弟關係嗎?
然而並非,由於我看了vue的文檔。他的意思就是父子通訊和非父子通訊。vuex

2、父子之間的通訊——Prop和自定義事件npm

組件實例的做用域是孤立的。這意味着不能而且不該該在子組件的模板內直接引用父組件的數據。
prop 是父組件用來傳遞數據的一個自定義屬性。子組件須要顯式地用 props 選項聲明 「prop」。
將咱們的App.vue看成父組件,將test看成子組件(什麼看成,原本就是)。
在App.vue中修改咱們的<test>:segmentfault

<!-- 傳遞一個say值 -->
   <test say="你是豬"></test>

在Test.vue中接收,並在頁面中顯示:瀏覽器

<template>
    <div>
        <p>我是全英雄聯盟最騷的騷豬</p>
           <p>說: {{say}}</p>    
           <!-- 綁定say值到頁面上 -->
    </div>
</template>

<script>
export default {
  name: 'test',
  props: ['say'] //這裏接收父組件穿過來的say值
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
p {
  color: red;
}
</style>

而後在瀏覽器的顯示效果以下:
圖片描述bash

綜上所述能夠看出,其實所謂的prop就是在<test></test>標籤添加一個自定義的屬性,而後在子組件中取出這個屬性,用Jquery也能夠作嘛(滿腦子,騷,騷想法.jpg)。app

上面的例子很漂亮的把父傳子的通訊方式展示出來了。可是子傳父呢?
vue文檔中使用的自定義事件。

使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件

咱們仍是用APP.vue做爲父組件,Test.vue做爲子組件
App.vue

...
/*增長一個位置來顯示子組件傳過來的值*/
<p>我兒子對我說: {{noSay}}</p>
/* 增長一個自定義的事件mychild,並給他指定觸發的方法*/
<test say="你是豬" v-on:myChild="toFatherSay"></test>
...
data () {
    return {
      noSay: '' // 用來接收子組件穿過來的數據
    }
  },
  methods: {
    toFatherSay: function(massage) { // mychlid事件觸發調用的方法
      this.noSay = massage // massage就是子組件穿過來的內容
    }
  }

Test.vue

....
/*增長一個按鈕,一點擊就向父組件傳值*/
<button v-on:click="toFather">點我傳給父組件</button>
....
data() {
      return {
          massage: '我纔不說呢' // 定義一個向父組件傳遞的值
      }
  },
  methods: {
      toFather: function() { // 按鈕點擊觸發的方法
          this.$emit('myChild',this.massage)// 使用$emit來向父組件傳播
      }
  },
....

作完以上操做以後在瀏覽器上測試:
圖片描述

3、非父子關係之間的通訊——eventBus

在veu文檔上,非父子之間的通訊是經過使用一個空的Vue實例做爲中央事件總線。
空的Vue實例? and 中央事件總線?
空的Vue實例也就是說

var bus = new Vue(); // 的確是一個空的

中央事件總線,難道組件通訊要經過全局的事件來進行?
的確是這樣,vue提供了$emit和$on方法來進行參數監聽(其實就是個發佈訂閱模式)。
建立一個空的Vue實例 Bus.js:

import Vue from 'vue'
export default new Vue();

將咱們的Apart.vue和Bpart.vue看成非父子關係組件:
Apart.vue

<template>
    <div>
        <p>我是Apart</p>
        <a v-on:click="goPage">點我切換</a>
    </div>
</template>

<script>
import Bus from '../Bus' //引入建立的Bus對象

export default {
  name: 'test',
  methods: {
      goPage: function () {
          Bus.$emit('whiteSay', '克里斯,關下門') // 使用$emit方法建立一個鍵值對
          this.$router.push('/bb')
      }
  },
  /*將引入的Bus組件注入咱們的組件對象中 import進來是不夠的 還要讓組件對象知道 */
  components: {
      Bus
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
p {
  color: red;
}
div {
    width: 100%;
    height: 100px;
    background-color: green;
}
</style>

Bpart.vue

<template>
    <div>
        <p>我是Bpart</p>
        <p>Apart對我說: {{whiteSay}}</p>
        <a v-on:click="goPage">點我切換</a>
    </div>
</template>

<script>
import Bus from '../Bus' // 一樣要引入Bus

export default {
  name: 'test',
  data () { // data用於存放組件的數據,必須是一個function,而且返回一個對象
      return {
          whiteSay: 'nihao'
      }
  },
  methods: {
      goPage: function () {
          this.$router.push('/')
      }
  },
  components: { // 一樣要注入Bus
      Bus
  },
  created: function() { // 在組件被建立時候將會執行此函數  至關於進入頁面的自執行
      Bus.$on('whiteSay', function(data) { // 使用$on方法監聽white屬性並執行一個回調函數
          this.whiteSay = data
          console.log(this.whiteSay)
      });
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
p {
  color: red;
}
div {
    width: 100%;
    height: 100px;
    background-color: yellow;
}
</style>

而後在瀏覽器中測試一下:
圖片描述

有問題!!!不管怎麼點擊咱們發現Bpart中定義的whiteSay並無改變,而且第一次點擊控制檯沒有打印。我在Bpart中寫了這段代碼:

data () {
      return {
          whiteSay: 'nihao'
      }
  },
  created: function() { // 在組件被建立時候將會執行此函數  至關於進入頁面的自執行
      Bus.$on('whiteSay', function(data) { // 使用$on方法監聽white屬性並執行一個回調函數
          this.whiteSay = data
          console.log(this.whiteSay)
      });
  }

按道理在元素被建立的時候,會將監聽到的值賦給whiteSay而且打印。可是咱們注意到第一次點擊,兩個操做都沒有執行,也就是說沒有監聽到whiteSay值的變化。而第二次以後都監聽到了。這是爲何?爲何把值賦給data中定義的whiteSay以後沒有網頁沒有更新?
帶着這兩個問題我去問了度娘和股哥。一下是答案:
第一個爲何: 項目中使用了vue-router,會先加載新的組件,等新的組件渲染好可是還沒掛載前,銷毀舊組件,在掛載新組件。將Apart.vue的代碼修改成:

...
 methods: {
      goPage: function () {
          this.$router.push('/bb')
      }
  },
  /*Vue 實例銷燬後調用 就是所謂的生命週期鉤子*/
  destroyed() {
    Bus.$emit('whiteSay', '克里斯,關下門') // 使用$emit方法建立一個鍵值對
    },
    ...

這樣第一個問題就解決了。附上找到的答案鏈接:https://segmentfault.com/q/10...
第二個爲何:這個是我本身代碼有問題,問了隔壁大神。說是個人做用域有問題,將Bpart.vue中的代碼改成:

···
 created: function() { // 在組件被建立時候將會執行此函數  至關於進入頁面的自執行
     var _self = this; // 將當前做用域保存在變量中,和$on()的做用域區分開來
      Bus.$on('whiteSay', function(data) { // 使用$on方法監聽white屬性並執行一個回調函數
          _self.whiteSay = data
          console.log(_self.whiteSay)
      });
  }
···

這樣全部的問題就都解決了。

4、Vuex
當我使用了上面幾種方法來實現組件的通訊存在着一些缺陷。好比父組件向子組件傳一個值,子組件將值處理完了返回給父組件,這將同時用到prop和自定義事件。還不如直接寫一個全部組件均可以訪問的變量呢來得方便呢。好比:

/*這是vuex文檔中的例子*/
const sourceOfTruth = {}
const vmA = new Vue({
  data: sourceOfTruth
})
const vmB = new Vue({
  data: sourceOfTruth
})

再好比當項目過大,組件之間的通訊將變得難以管理。veux的初衷就是爲什麼更好的管理組件的狀態。一下是vuex文檔對vuex的定義:

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。

寫得好累,還好最近沒事作,不會被boss罵。

接下來直接開始使用vuex。

先下載
在根目錄下打開cmd:

npm install vuex -save

下載成功看到一下數據:

C:\Users\59227\Desktop\x-chat>npm install vuex --save
x-chat@1.0.0 C:\Users\59227\Desktop\x-chat
`-- vuex@2.1.1

npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.15

而後在main.js中引用,並安裝到Vue上面

import Vuex from 'vuex'

Vue.use(Vuex)

前面兩步將Vuex引入到了項目當中,接下來如何使用Vuex。
Vuex的核心是一個store(倉庫)這個倉庫的做用就是用來管理應用中的state(狀態)。這裏狀態該怎麼理解?
我我的的理解是:全部組件共享的並能夠進行更改的對象。
除了state的,store還有getter、Mutations、Actions以及Modules。在vuex文檔中都有很是詳細的說明:http://vuex.vuejs.org/zh-cn/s...
籠統的說:

組件獲取 state 用 vuex 的 getter
組件觸發動做用 vuex 的 action
修改 state 用 vuex 的 mutation

知乎上看到的,說得很貼切易懂。

直接上代碼,建議擼完代碼,再去看一遍vuex的文檔。

main.js

....
const store = new Vuex.Store({ //建立一個倉庫
    state: {  
        showDagger: true, // 定義一個狀態
    },
    mutations: {// 定義 mutation ,更改 Vuex 的 store 中的狀態的惟一方法是提交mutation
        daggerCtrl (state) { // 必定要傳入state,而且是第一個參數
            state.showDagger = !state.showDagger  // 將showDagger值取反
        }
    }
})
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router, // 將router對象傳給vue,這樣就能夠經過this.$router獲取到router對象了
  store, // 將store對象傳給vue,這樣就能夠經過this.$store獲取到store對象了
  template: '<App/>', 
  components: { App }
})

而後更改App.vue:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <p>我兒子對我說: {{noSay}}</p>
    <test say="你是豬" v-on:myChild="toFatherSay"></test>
    <router-view></router-view>
    <!-- @click是v-on:click的簡寫方式 -->
    <button @click="changeDagger">dagger</button>       1.添加按鈕和組件
    <dagger></dagger>
  </div>
</template>

<script>
import Test from './components/Test'  // 這裏引入Test組件
import Dagger from './components/Dagger' // 引入Dagger組件        2.引入Dagger

export default {
  name: 'app',
  components: {
    Test, // 在components中添加Test
    Dagger                                                3.注入Dagger
  },
  data () {
    return {
      noSay: ''
    }
  },
  methods: {
    toFatherSay: function(massage) {
      this.noSay = massage
    },
    changeDagger: function() {                       4.增長按鈕點擊觸發的事件
      this.$store.commit('daggerCtrl') // 使用commit(提交)方法喚醒名爲daggerCtrl的mutation
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

dagger.vue

<template>
    <div class="dagger" v-if="this.$store.state.showDagger">
        <h1>Dagger</h1>
    </div>
</template>

<script>

</script>

<style scoped>
.dagger {
    margin: 0 auto;
    width: 50%;
    height: 100px;
    background-color: red;
}
</style>

打開瀏覽器 看效果:
圖片描述

使用vuex實現組件通訊就搞定了,更多的用法請參考vuex文檔。

相關文章
相關標籤/搜索