夫天地者,万物之逆旅;光阴者,百代之过客。而浮生若梦,为欢几何?
Vue.js项目实战三:非父子组件间通信

前言

前面我们已经学习了vue.js中父子组件之间的通信,所谓非父子组件,即两个相互独立的组件,比如在Home组件中有两个组件分别叫component-a和component-b,他们之间没有谁包含谁的关系,可以看作是兄弟组件,那么这两个组件之间应该如何通信呢?下面将通过两种方式来实现。

eventBus

所谓eventBus就是创建一个事件中心,相当于一个中转站,可以用它来传递事件和接收事件。首先我们需要先声明一个空的Vue模块eventBus

eventBus.js

import Vue from 'vue'
//定义空的vue实例,作为 eventbus实现非父子组件之间的通信(vue2.x中去掉了broadcast)
var eventBus = new Vue();
export default eventBus;

下面将分别定义A组件和B组件,以及他们的父组件Home组件

组件A:html代码

<template>
  <div id="aComp">
    <h2>我是A组件:</h2>
    <span>{{messageFromA}}</span>
    <br>
    <button @click="sendMessageToB">点击我向B组件发送消息</button>
  </div>
</template>

组件A:JavaScript代码

<script>
  import eventBus from '../model/eventBus.js' //引入eventBus.js
  export default{
    data(){
      return {
        messageFromA:'我是A组件中,向B组件发送消息',
      }
    },
    methods:{
      sendMessageToB(){
        eventBus.$emit('sendToB',this.messageFromA);
      }
    },
    mounted(){
      let that = this;
      eventBus.$on('sendToA',function (message) {
        console.log(message);
        that.messageFromA = message;
      })
    }
  }
</script>

组件B:html代码

<template>
  <div id="bComp">
    <h3>我是B组件:</h3>
    <span>{{messageFromB}}</span>
    <br/>
    <button @click="sendMessageToA">点击我向A组件发送消息</button>
  </div>
</template>

组件B:JavaScript代码

<script>
  import eventBus from '../model/eventBus.js' //引入eventBus.js
  export default {
    data(){
      return {
        messageFromB:'我是B组件',
        sendMsgToB:'我是B组件,向A组件发送消息'
      }
    },
    mounted(){
      let that = this;
      eventBus.$on('sendToB',function (message) {
        console.log(message);
        that.messageFromB = message;
      })
    },
    methods:{
      sendMessageToA(){
        eventBus.$emit('sendToA',this.sendMsgToB);
      }
    }
  }
</script>

到这里,我们就可以实现A组件和B组件之间的通信,具体的效果如下:

vuex

在使用vuex之前,我们需要先了解一下什么是“单页面应用(SPA)”,

单页面应用(SPA)通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。

第一次进入页面的时候会请求一个html文件,刷新清除一下。切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了。

原理是:JS会感知到url的变化,通过这一点,可以用js动态的将当前页面的内容清除掉,然后将下一个页面的内容挂载到当前页面上,这个时候的路由不是后端来做了,而是前端来做,判断页面到底是显示哪个组件,清除不需要的,显示需要的组件。这种过程就是单页应用,每次跳转的时候不需要再请求html文件了。

多页面应用(MPA)就是指一个应用中有多个页面,页面跳转时是整页刷新。每次跳转都需要发出一个http请求,服务器返回一个新的对应的html页面。

下面回到我们的主题vuex。vuex是为了解决同一个页面多个组件之间的通信的,也就是说同一个页面里不同组件需要用到同一份数据的时候才是vuex数据持久化的用场。而我们的vue开发的项目更多的采用组件化开发的思想,是单页面应用。

通过前面的学习我们发现,实现多个组件之间的通信,虽然方法有很多,但多十分繁琐。而Vuex很好的解决了组件之间同一状态的数据共享问题,实现了组件里数据的持久化,是一个专为Vue.js应用程序开发的状态管理模式。

使用前 仍然需要安装vuex

npm install vuex --save / cnpm install vuex --save

具体的使用如下,首先定义一个store.js:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//state在vuex中用于存储数据
var state={
    count:1
}
//mutations里面放的是方法,方法主要用于改变state里面的数据
var mutations={
    incCount(){
        ++state.count;
    }
}
//vuex  实例化 Vuex.store
const store = new Vuex.Store({
    state,
    mutations
})
export default store;

home组件 html代码

<template>
 <div id="home">
   <br>
   我是home组件  -- {{this.$store.state.count}}
   <br>
   <button @click="incCount()">增加数量+</button>
 </div>
</template>

home组件 JavaScript代码

<script>
  import store from '../vuex/store.js'
  export default{
    data(){
      return { }
    },
    store,
    methods:{
      incCount(){
        //改变vuex store里面的数据
        this.$store.commit('incCount'); //触发 mutations 改变 state里面的数据
      }
    }
  }
</script>

在news.vue组件里也拷贝相同的代码,这里不在贴出,直接看演示的结果

vuex有五个核心属性:state, getters, mutations, actions, modules

state:vuex的基本数据,用来存储变量。

getters:从基本数据(state)派生的数据,相当于vue中的computed(计算属性) , 都是用来计算 state 然后生成新的数据 ( 状态 ) 的。

mutations:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。

回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。

actions:和mutations的功能大致相同。多个 state 的操作,使用 mutations 会来触发会比较好维护 , 那么需要执行多个 mutations 就需要用 action 了,不同之处在于:

   1、Action 提交的是 mutation,而不是直接变更状态。 

   2、Action 可以包含任意异步操作。

modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。

项目示例代码

下面贴出本项目中使用的vuex封装的部分代码,不在详细剖析,因为网上可以看到很多相关文档。

mutation-types.js

export const GET_ARTICLE_LIST = 'GET_ARTICLE_LIST';
export const GET_CATEGORY_LIST = 'GET_CATEGORY_LIST';

actions.js

import axios from 'axios'
import * as types from './mutation-types.js'
export default {
  getArticleList({commit,state}){
    axios.get('/api/articleList').then((response)=>{
      let result = response.data.data;
      if(result){
        commit(types.GET_ARTICLE_LIST,result);
      }
    })
  },
  getCategoryList({commit,state}){
    axios.get('/api/findCategoryBy').then((response)=>{
      let result = response.data.data;
      console.log(result);
      if(result){
        commit(types.GET_CATEGORY_LIST,result);
      }
    })
  }
}

mutations.js

import{
  GET_ARTICLE_LIST,
  GET_CATEGORY_LIST
} from './mutation-types.js'
export default {
  [GET_ARTICLE_LIST](state,docs){
    state.articlelist = docs;
  },
  [GET_CATEGORY_LIST](state,docs){
    state.categorylist = docs;
  }
}

getters.js

export default {
  articlelist:state => state.articlelist,
  categorylist:state => state.categorylist
}

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations.js'
import actions from './actions.js'
import getters from './getters.js'
Vue.use(Vuex);
const state={
  articlelist:[],
  categorylist:[]
}
export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})

在组件里如何使用?实际示例代码如下:

<template>
  <div class="index">
   <slider-bar :hotarticles="hotarticles" :categorylist="categorylist"></slider-bar>
  </div>
</template>
<script>
  import {mapState,mapActions} from 'vuex'
  import sliderBar from '../../components/sliderBar'
  export default {
    data(){
      return{}
    },
    computed:{
      ...mapState(['hotarticles','categorylist'])//...三点叫做扩展运算符,相当于 return this.$store.state.hotarticles
    },
    mounted(){
      this.getHotArticles();
      this.getCategoryList();
    },
    methods:{
      ...mapActions(['getHotArticles','getCategoryList'])//mapState、mapGetters、mapActions等可以简化vuex写法
    },
    components:{
      sliderBar
    }
  }
</script>

总结

笔者在写这篇文章的时候,有许多概念性的东西理解不是很深刻,所以也查阅了很多相关文档,边理解边记录,逐步加深理解。下面整理一些个人认为比较好的文章,以供参考。

https://zhuanlan.zhihu.com/p/24357762

https://segmentfault.com/a/1190000009404727

https://www.jianshu.com/p/9f375f3512d5


作者:一蓑烟雨

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

0

支持

0

反对

posted @2020-3-18  拜读(437)

评论列表

评论内容:



喜欢请打赏

支付宝 微信

请放心支付