vue2.0开发聊天程序(三)组件的通信

天地不仁以万物为刍狗,宇宙无义视众生如蝼蚁
                  ——萧鼎和我

上一节列出了5个关键点,第一个路由已经解决了,接下来解决第二个问题:

组件的通信问题

一、组件的关系

组件之间的关系无非就是两种父子关系没有父子关系。为什么我这样说呢?
按道理应该还有兄弟关系(也就是并列的组件,比如一个组件中引用了hreder和footer组件。),还有爷孙关系(比如我有七个Calabash Brothers组件,放在的HanHan组件下,而HanHan组件放在了Chairman Mao组件下)
那么不应该是父子、爷孙、兄弟关系吗?
然而并不是,因为我看了vue的文档。他的意思就是父子通信和非父子通信。

二、父子之间的通信——Prop和自定义事件

组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。
prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props 选项声明 “prop”。
将我们的App.vue当作父组件,将test当作子组件(什么当作,本来就是)。
在App.vue中修改我们的<test>:

    <!-- 传递一个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>

然后在浏览器的显示效果如下:
图片描述

综上所述可以看出,其实所谓的prop就是在<test></test>标签添加一个自定义的属性,然后在子组件中取出这个属性,用Jquery也可以做嘛(满脑子,骚,骚想法.jpg)。

上面的例子很漂亮的把父传子的通信方式展现出来了。但是子传父呢?
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来向父组件传播
      }
  },
....

做完以上操作之后在浏览器上测试:
图片描述

三、非父子关系之间的通信——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)
      });
  }
···

这样所有的问题就都解决了。

四、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文档。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值