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>
- 1
- 2
- 3
- 4
- 5
然后便可以在父组件中通过 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);
}
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
点击父组件便可以在控制台上看到子组件的输出了
可以在 这里 看看效果。
子组件和父组件通信
首先在父组件使用 $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>
- 1
- 2
- 3
- 4
- 5
然后在子组件中触发父组件事件,动态更改计数
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
}
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
这里父子组件通信,父组件通过 props
向子组件传值,子组件通过自定义是事件与父组件通信
可以在 这里 看看效果。
兄弟组件通信
若 A
组件想要和 B
组件通信,按照父子组件通信的思维,则是 A
和父组件通信,然后父组件再和 B
组件通信,比较绕。有没有更好的方法呢,事实上,Vue
提供了个 Event
Bus
(中央事件总线)的机制。
let bus = new Vue();
- 1
// 在A组件中触发组件 B 中的事件
bus.$emit("eventB", "A")
- 1
- 2
// 在组件 B 创建的钩子中监听事件
bus.$on("eventB", function (msg) {
console.log(msg)
})
- 1
- 2
- 3
- 4
可以在 这里 看看效果。
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
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")
}
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
最后来个在实际项目中 Vuex
实例,需要安装 npm install
vuex vuex-persistedstate
, vuex-persistedstate
实际上使用了 HTML
5
中的 localStorage
来做缓存,这样在刷新页面后之前 state
的数据不会丢失。
- mutation-type.js
// 更新 count
export const UPDATE_COUNT = "UPDATE_COUNT";
- 1
- 2
- mutation.js
import * as types from "./mutation-types"
export default {
// 更新 count
[types.UPDATE_COUNT] (state, count) {
state.count= count
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- actions.js 【需注意在
mutations
里都是用大写下划线连接,在actions
里都用小写驼峰对应】
import * as types from "./mutation-types"
export default {
updateCount({ commit }, count) {
commit(types.UPDATE_COUNT, count)
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- getters.js
export default {
evenOrOdd: state => state.count % 2 === 0 ? "even" : "odd"
}
- 1
- 2
- 3
- 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()]
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- main.js
import store from "./store"
new Vue({
el: "#app",
store,
template: "<App/>",
components: { App }
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 在具体某个组件中应用
<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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
如果项目较大,而且操作的状态又多,要是都放在一棵树上,那么 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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
引用
this.$store.state.app.count // 加上模块名
this.$store.dispatch("updateCount", 1)