父子组件通信
父组件和子组件通信
使用 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)