Vue组件通信

父子组件通信

父组件和子组件通信

使用 ref 为子组件指定一个索引 ID

<div id="counter-event-example">
  <p @click="clickB">{{ msg }}</p>
  <button-counter-a ></button-counter-a>
  <button-counter-b ref="refB"></button-counter-b>
</div>

然后便可以在父组件中通过 this.$refs.refB 访问子组件方法了

Vue.component('button-counter-a', {
  template: '<button v-on:click="eventClick">{{ msg }}</button>',
  data: function () {
    return {
      msg: '组件A'
    }
  },
  methods: {
    eventClick: function () {
    }
  }
})

Vue.component('button-counter-b', {
  template: '<button>{{ msg }}</button>',
  data: function () {
    return {
      msg: '组件B'
    }
  },
  methods: {
    eventClick: function (msg) {
      console.log(msg)
    }
  }
})

new Vue({
  el: '#counter-event-example',
  data: {
    msg: '父组件'
  },
  methods: {
    clickB () {
      this.$refs.refB.eventClick(this.msg);
    }
  }
})

点击父组件便可以在控制台上看到子组件的输出了

可以在 这里 看看效果。

子组件和父组件通信

首先在父组件使用 $on(eventName) 监听, 然后在子组件 $emit(eventName) 触发事件。

官网 中的计数器实例,先是在父组件上监听事件 incrementTotal

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter :count="count" v-on:increment="incrementTotal"></button-counter>
  <button-counter :count="count" v-on:increment="incrementTotal"></button-counter>
</div>

然后在子组件中触发父组件事件,动态更改计数

Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  props : ["count"],
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment', this.counter) // 第二个参数,可以传递到父组件中
      console.log(this.count) // 调用父组件方法,然后发现count值变了,这里相当于接收返回值了
    }
  }
})

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0,
    count: 0
  },
  methods: {
    incrementTotal: function (count) {
      console.log(count) // 接收子组件中的参数
      this.count += 1
      this.total += 1
    }
  }
})

这里父子组件通信,父组件通过 props 向子组件传值,子组件通过自定义是事件与父组件通信

可以在 这里 看看效果。

兄弟组件通信

A 组件想要和 B 组件通信,按照父子组件通信的思维,则是 A 和父组件通信,然后父组件再和 B 组件通信,比较绕。有没有更好的方法呢,事实上,Vue 提供了个 Event Bus (中央事件总线)的机制。

bus.js

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

在A组件中触发组件 B 中的事件

import bus from 'bus.js';
bus.$emit('eventB', 'A')

在组件 B 创建的钩子中监听事件

import bus from 'bus.js';
bus.$on('eventB', function (msg) {
   console.log(msg)
 })

可以在 这里 看看效果。

Vuex

对于复杂的组件通信, 还是推崇 Vue 专门用来状态管理模式的 Vuex。来看看下面官网上的这张图

这里写图片描述

图中的 State, 就是驱动应用的原始数据源,以声明方式将这些 State 映射到 View 中,而要改变 State,则要通过一些 Action 来驱动。

这里写图片描述

图中绿色虚线 Vuex 部分显示发生在 Component 上的一次事件中来 Commit 一个相应的 Mutations 方法,Mutations 方法来 Mutate 对应的 State,然后在组件中通过计算属性的监测,响应式更新视图。

来看个简单的计数器 实例

<div id="app">
  <p>{{ count }}</p>
  <p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </p>
</div>
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => state.count++, // 箭头函数,相当于 function (state) {return state.count++}
    decrement: state => state.count--
  }
})

new Vue({
  el: '#app',
  computed: {
    count () {
        return store.state.count
    }
  },
  methods: {
    increment () {
      store.commit('increment')
    },
    decrement () {
        store.commit('decrement')
    }
  }
})

最后来个在实际项目中 Vuex 实例,需要安装 npm install vuex vuex-persistedstate , vuex-persistedstate 实际上使用了 HTML 5 中的 localStorage 来做缓存,这样在刷新页面后之前 state 的数据不会丢失。

  • mutation-type.js
// 更新 count
export const UPDATE_COUNT = 'UPDATE_COUNT';
  • mutation.js
import * as types from './mutation-types'

export default {
    // 更新 count
  [types.UPDATE_COUNT] (state, count) {
    state.count= count
  }
}
  • actions.js 【需注意在 mutations 里都是用大写下划线连接,在 actions 里都用小写驼峰对应】
import * as types from './mutation-types'

export default {
  updateCount({ commit }, count) {
    commit(types.UPDATE_COUNT, count)
  }
}  
  • getters.js
export default {
    evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}
  • state 【index.js】
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

Vue.use(Vuex);

const state = {
    count: 0
};

export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters,
    plugins: [createPersistedState()]
})
  • main.js
import store from './store'

new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})
  • 在具体某个组件中应用
<template>
    <div @click="increment">{{count}} {{isEvenOrOdd}}</div>
</template>

<script>
  export default {
    name: 'bar',
    computed: {
      count () {
        return this.$store.state.count
      },
      isEvenOrOdd () {
        return this.$store.getters.evenOrOdd
      }
    },
    methods: {
      increment () {
        this.count += 1
        this.$store.dispatch('updateCount', this.count)
      }
    }
  }
</script>

如果项目较大,而且操作的状态又多,要是都放在一棵树上,那么 store 对象就先显得臃肿,这时可以可以把 store 按一定逻辑分割成 module (模块)

  • app.js
const app = {
    state: {
        count: 0
    },
    mutations: {
        UPDATE_COUNT: (state, count) => {
            state.count += count
        }
    },
    actions: {
        updateCount({ commit }, count) {
            commit('UPDATE_COUNT', count)
        }
    }
    getters: {
        evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
    }
}

export default app;
  • store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate'
import app from './modules/app';
Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    app
  },
  plugins: [createPersistedState()]
});

export default store

引用

this.$store.state.app.count // 加上模块名
this.$store.dispatch('updateCount', 1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值