EventBus又称事件总线,在Vue中可以使用EventBus来作为沟通桥梁的概念,就像是所有事件共用相同的事件中心,可以向该中心发送事件或接收事件,所有组件都可以上下平行的通知其他组件,但太方便若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次
1.初始化
首先你需要做的是将事件总线并将其导出,以便其他模块可以使用或者监听它。我们可以用两种方式来处理:
第一种是,新建一个.js文件,比如:event-bus.js:
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
第二种是,直接在项目的main.js种初始化Event Bus:
// main.js
Vue.prototype.$EventBus = new Vue()
这种方式初始化的 EventBus
是一个 全局的事件总线 。
注意,这种方式初始化的
EventBus
是一个全局的事件总线
。稍后再来聊一聊全局的事件总线。
2.发送事件
假设你有两个Vue页面需要通信: A 和 B ,A页面 在按钮上面绑定了点击事件,发送一则消息,想=通知 B页面。
<!-- A.vue -->
<template>
<button @click="sendMsg()">-</button>
</template>
<script>
import { EventBus } from "../event-bus.js";
export default {
methods: {
sendMsg() {
EventBus.$emit("aMsg", '来自A页面的消息');
}
}
};
</script>
接下来,我们需要在 B页面 中接收这则消息。
3.接收事件
<!-- B.vue -->
<template>
<p>{{msg}}</p>
</template>
<script>
import { EventBus } from "../event-bus.js";
export default {
data(){
return {
msg: ''
}
},
mounted() {
EventBus.$on("aMsg", (msg) => {
// A发送来的消息
this.msg = msg;
});
}
};
</script>
同理我们也可以在 B页面 向 A页面 发送消息。这里主要用到的两个方法:
// 发送消息
EventBus.$emit(channel: string, callback(payload1,…))
// 监听接收消息
EventBus.$on(channel: string, callback(payload1,…))
前面提到过,如果使用不善,EventBus
会是一种灾难,到底是什么样的“灾难”
了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus
会被移除,这样就导致业务走不下去。还要就是如果业务有反复操作的页面,EventBus
在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理 EventBus
在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus
事件监听。
移除事件监听者
如果想移除事件的监听,可以像下面这样操作:
import {
eventBus
} from './event-bus.js'
EventBus.$off('aMsg', {})
你也可以使用 EventBus.$off('aMsg')
来移除应用内所有对此某个事件的监听。或者直接调用 EventBus.$off()
来移除所有事件频道,不需要添加任何参数 。
上面就是 EventBus
的使用方法,是不是很简单。上面的示例中我们也看到了,每次使用 EventBus
时都需要在各组件中引入 event-bus.js
。事实上,我们还可以通过别的方式,让事情变得简单一些。那就是创建一个全局的 EventBus
。接下来的示例向大家演示如何在Vue项目中创建一个全局的 EventBus
。
<!-- B.vue -->
<template>
<!-- 展示msgB -->
<p>{{msgB}}</p>
</template>
<script>
export default {
data(){
return {
//初始化一个msgB
msgB: ''
}
},
mounted() {
/*调用全局Vue实例中的$EventBus事件总线中的$on属性,监听A组件发送
到事件总线中的aMsg事件*/
this.$EventBus.$on("aMsg", (data) => {
//将A组件传递过来的参数data赋值给msgB
this.msgB = data;
});
},
beforeDestroy(){
//移除监听事件"aMsg"
this.$EventBus.$off("aMsg")
}
};
</script>
使用前提:vuex是使用vue中必不可少的一部分,基于父子,兄弟组件,我们传值可能会比较方便,但是没有关联的组件之间使用同一组数据,就显得无能为力,那么vuex就解决了这个问题
相当于一个公共的仓库,保存着所有组件都能用的公用数据
在新建的js文件中写入一下代码
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex);
export default new Vuex.Store({
state:{
pathName: "",
currDbSource: {},
currJobData: {},
DbSource: []
},
mutations:{
// 保存当前菜单栏的路径
savePath(state,pathName){
state.pathName = pathName;
},
// 保存当前点击的数据源
saveCurrDbSource(state,currDbSource){
state.currDbSource = currDbSource;
},
// 保存当前点击的元数据
saveCurrJobData(state,currJobData){
state.currJobData = currJobData;
},
// 保存所有数据源
saveDbSource(state,DbSource){
state.DbSource = DbSource;
}
}
})
这里解释一下各个代码段的作用:state是自定义的一些变量,需要用来保存数据,mutations来触发事件,相当于方法,用户需要通过触发这个方法,借此来保存数据,参数的话,第二个参数就是用户传入的值,然后在方法中赋值给state中的变量
// 引入vuex-store
import store from './store/index';
new Vue({
el: '#app',
router,
store,
render: h => h(App)
});
methods:{
click(){
// 点击按钮进行一些操作,然后保存数据
this.$store.commit('saveCurrDbSource',this.db)
}
}
这里的第一个参数是要触发的方法,也就是上面mutations中的方法,第二个参数是你要传递的数据
this.$store.state.变量名
// 例如
this.store.state.currDbSource
这样其他组件就可以共用这个保存起来的数据了,也能进行相应的修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1l2SEujG-1612086600969)(E:\Typora笔记\imgs\1612083409403.png)]
路由如下 :
import Vue from 'vue'
import Router from 'vue-router'
import componentsA from '@/components/componentsA'
import componentsB from '@/components/componentsB'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'componentsA',
component: componentsA
},
{
path: '/componentsA',
name: 'componentsA',
component: componentsA
},
{
path: '/componentsB',
name: 'componentsB',
component: componentsB
}
]
})
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</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>
*as的意思是 导入这个文件里面的所有内容,就不用一个个实例来导入了。
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 导入响应的模块,*相当于引入了这个组件下所有导出的事例
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)
// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
const state = {
resturantName: '天下第一客栈' // 默认值
// id: xxx 如果还有全局状态也可以在这里添加
// name:xxx
}
// 注册上面引入的各大模块
const store = new Vuex.Store({
state, // 共同维护的一个状态,state里面可以是很多个全局状态
getters, // 获取数据并渲染
actions, // 数据的异步操作
mutations // 处理数据的唯一途径,state的改变或赋值只能在这里
})
export default store // 导出store并在 main.js中引用注册。
// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
return commit ('modifyBName', name)
}
// ES6精简写法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)
// 提交 mutations是更改Vuex状态的唯一合法方法
export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
state.resturantName = name // 把方法传递过来的参数,赋值给state中的resturantName
}
export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
state.resturantName = name
}
// 获取最终的状态信息
export const resturantName = state => state.resturantName
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store, // 这样就能全局使用vuex了
components: { App },
template: '<App/>'
})
…mapactions 和 …mapgetters都是vuex提供的语法糖,在底层已经封装好了,拿来就能用,简化了很多操作。
其中…mapActions([‘clickAFn’]) 相当于this.$store.dispatch(‘clickAFn’,{参数}),mapActions中只需要指定方法名即可,参数省略。
…mapGetters([‘resturantName’])
相当于this.$store.getters.resturantName
<template>
<div class="componentsA">
<P class="title">组件A</P>
<P class="titleName">餐馆名称:{{resturantName}}</P>
<div>
<!-- 点击修改 为 A 餐馆 -->
<button class="btn" @click="modifyAName('A餐馆')">修改为A餐馆</button>
</div>
<div class="marTop">
<button class="btn" @click="trunToB">跳转到B页面</button>
</div>
</div>
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
name: 'A',
data () {
return {
}
},
methods:{
...mapActions( // 语法糖
['modifyAName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法
),
trunToB () {
this.$router.push({path: '/componentsB'}) // 路由跳转到B
}
},
computed: {
...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.title,.titleName{
color: blue;
font-size: 20px;
}
.btn{
width: 160px;
height: 40px;
background-color: blue;
border: none;
outline: none;
color: #ffffff;
border-radius: 4px;
}
.marTop{
margin-top: 20px;
}
</style>
<template>
<div class="componentsB">
<P class="title">组件B</P>
<P class="titleName">餐馆名称:{{resturantName}}</P>
<div>
<!-- 点击修改 为 B 餐馆 -->
<button class="btn" @click="modifyBName('B餐馆')">修改为B餐馆</button>
</div>
<div class="marTop">
<button class="btn" @click="trunToA">跳转到A页面</button>
</div>
</div>
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
name: 'B',
data () {
return {
}
},
methods:{
...mapActions( // 语法糖
['modifyBName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法
),
trunToA () {
this.$router.push({path: '/componentsA'}) // 路由跳转到A
}
},
computed: {
...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.title,.titleName{
color: red;
font-size: 20px;
}
.btn{
width: 160px;
height: 40px;
background-color: red;
border: none;
outline: none;
color: #ffffff;
border-radius: 4px;
}
.marTop{
margin-top: 20px;
}
</style>
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.title,.titleName{
color: red;
font-size: 20px;
}
.btn{
width: 160px;
height: 40px;
background-color: red;
border: none;
outline: none;
color: #ffffff;
border-radius: 4px;
}
.marTop{
margin-top: 20px;
}
</style>
因篇幅问题不能全部显示,请点此查看更多更全内容