Vue状态管理之Bus

一般在项目中,状态管理都是使用Vue官方提供的Vuex

当在多组件之间共享状态变得复杂时,使用Vuex,此外也可以使用Bus来进行简单的状态管理

1.1 父组件与子组件之间的通信

vue.config.js文件内容

const path = require('path')
const resolve = dir => path.join(__dirname,dir)
const BASE_URL = process.env.NODE_ENV === 'production' ? '/iview-admin':'/'

module.exports = {
    lintOnSave: false,
    baseUrl: BASE_URL,
    chainWebpack:config =>{
        config.resolve.alias
        .set('@',resolve('src'))    // 用 @ 符号来替代 src 这个路径
        .set('_c',resolve('src/components'))        // 用 _c 来替代 src/components这个目录
    },
    productionSourceMap:false,       // 打包时不生成 .map文件,减少打包的体积同时加快打包速度
    devServer:{         // 跨域配置,告诉开发服务器将任何没有匹配到静态文件的请求都代理到proxy指定的URL
        proxy:'http://localhost:8000'
    }
}

src/components/AInput.vue文件内容

<template>
    <input @input="handleInput" :value="value">
</template>
<script>
export default {
    name:"AInput",
    props:{
        value:{
            type:[String,Number],
            default:''
        }
    },
    methods:{
        handleInput (event){
            const value = event.target.value
            this.$emit('input',value)
        }
    }
}
</script>

src/views/store.vue文件内容

<template>
    <div>
        <a-input v-model="inputValue"></a-input>
        <p>{{inputValue}}</p>
    </div>
</template>

<script>
import AInput from '_c/AInput.vue'      // 引入 AInput组件

export default {
    name:'store',
    data () {
        return {
            inputValue:''
        }
    },
    components:{
        AInput      // 注册AInput组件,然后就可以使用 AInput组件了
    }
}
</script>

src/router/router.vue文件内容

import Home from '@/views/Home.vue'

export default [
    ...     // 此处省略
    {
        path:'/store',
        component:() => import('@/views/store.vue')
    }
]

浏览器打开URL:http://localhost:8080/#/store,显示效果如下

首先在AInput.vue文件中,为input标签绑定handleInput事件当input框中的数据改变时就会触发handleInput事件,input标签中显示的数据就是value的值

store.vue文件中,a-input标签使用v-model进行双向绑定,v-model是一个语法糖

v-model的效果等同于如下
src/views/store.vue文件内容

<template>
    <div>
        <a-input :value="inputValue" @input="handleInput"></a-input>
        <p>{{inputValue}}</p>
    </div>
</template>

<script>
import AInput from '_c/AInput.vue'

export default {
    name:'store',
    data () {
        return {
            inputValue:''
        }
    },
    components:{
        AInput
    },
    methods:{
        handleInput(val){
            this.inputValue = val
        }
    }
}
</script>

相比于上面的代码方式,v-model方便很多

vue中,单向数据流通信:

父组件向子组件传值一定是通过属性,子组件想修改父组件传递的值时,一定要通过事件,把要修改的值以参数的形式通过事件提交给父组件,然后在父组件中绑定事件接收消息知道子组件要修改父组件中的数据,最后就可以在子组件中修改数据了,这就是父子组件之间的通信

1.2 单页面中多组件(兄弟组件)中的通信

src/components目录下新建AShow.vue文件
src/components/AShow.vue文件内容

<template>
    <div>
        <p>AShow: {{ content }}</p>
    </div>
</template>
<script>
export default {
    props: {
        content: {
            type: [String, Number],
            default: ''     // content的值默认为空
        }
    }
}
</script>

修改src/components/store.vue文件内容

<template>
    <div>
        <a-input @input="handleInput"></a-input>
        <a-show :content="inputValue"></a-show>
    </div>
</template>
<script>
import AInput from '_c/AInput.vue'      // 引入 AInput组件
import AShow from '_c/AShow.vue'      // 引入 AShow组件

export default {
    name:'store',
    data () {
        return {
            inputValue:''
        }
    },
    components:{
        AInput,      // 注册AInput组件,然后就可以使用 AInput组件了
        AShow       // 注册AShow组件
    },
    methods: {
        handleInput(val){
            this.inputValue = val
        }
    }
}
</script>

浏览器打开URL: http://localhost:8080/#/store,显示效果如下

可以看到,在父组件store中给子组件AInput绑定事件handleInput,handleInput触发之后,把AInput组件中输入的数据赋值给this.inputValue

最后再把this.inputValue的值传递给AShow组件的content属性,以达到单页面下多组件传值的目的。

1.3 使用Bus进行多组件的通信

src/lib目录下新建'bus.js'文件
src/lib/bus.js文件内容

import Vue from 'vue'
const Bus = new Vue()
export default Bus

src/main.js文件中引入bus.js文件
src/main.js文件内容

import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/index'
import Bus from '@/lib/bus'     // 引入Bus组件

Vue.config.productionTip = false
Vue.prototype.$bus = Bus    // 注册到vue的根实例里,给vue原生对象上添加bus属性

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

src/views/view1.vue文件内容

<template>
    <div class="div2">
        <button @click="handleClick" name="button">按钮一</button>
    </div>
</template>
<script>
    export default {
        methods: {
            handleClick() {
                this.$bus.$emit('changeValue', 'hello')  // 把on-click事件提交给 bus
            }
        },
        mounted () {
            console.log(this.$bus)      // 打印出 this.$bus对象
        }
    }
</script>
<style>
    .div2 {
        border: 1px solid green;
    }
</style>

src/views/view2.vue文件内容

<template>
    <div class="div1">
        <p>{{ message }}</p>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                message: ''
            }
        },
        mounted() {
            this.$bus.$on('changeValue', msg => {
                this.message = msg
            })  // 监听bus的 on-click 事件
        }
    }
</script>
<style>
    .div1 {
        border:1px solid red;
    }
</style>

src/router/router.js文件内容

import Home from '@/views/Home.vue'

export default [
    {
        path:'/named_view',
        components:{
            default: () => import('@/views/child.vue'),
            view1: () => import('@/views/view1.vue'),
            view2: () => import('@/views/view2.vue'),
        }
    }
    ...
]

浏览器打开URL: http://localhost:8080/#/named_view,在浏览器的调试工具中可以看到打印的this.$bus对象

页面渲染结果为

点击页面上的'按钮一',效果如下

在上面的例子里,按钮一所有的绿色框所在就是view1组件,红色框所在就是view2组件

view1.vue组件中,$emit方法会在当前组件view1上把changeValue事件changeValue是绑定在this.$bus这个vue实例上,然后获取changeValue事件的返回值'hello'

然后在view2组件中,在this.$bus这个实例上监听changeValue事件,得到changeValue事件的返回值'hello',然后把返回值赋值给 message,并在p标签上显示出来,这样就实现了不同组件之间的通信。

posted @ 2019-07-18 21:48  renpingsheng  阅读(4721)  评论(0编辑  收藏  举报