Vue——组件通讯

1.自定义事件

组件关系可分为父子组件通信、兄弟组件通信、跨级组件通信。
从父组件像向子组件通信,通过props传递数据就可以了。

当子组件需要向父组件传递数据时,就要用到自定义事件。
v-on除了监听DOM事件外,还可以用于组件之间的自定义事件。
子组件用$emit()来触发事件,父组件用$on()来监听子组件的事件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app">
    <p>总数: {{ total }}</p>
    <!--定义两个自定义事件-->
    <my-component @increase="handleGetTotal"
                  @reduce="handleGetTotal"></my-component>
    <!--两个自定义事件向一个函数传参,这样能保证counter一致-->
</div>

<script>
    Vue.component('my-component', {
        template: '' +
        '<div>' +
        '<button @click="handleIncrease">+1</button>' +
        '<button @click="handleReduce">-1</button>' +
        '</div>',
        // 组件绑定两个方法,这两个方法肯定是需要的
        data: function () {
            return {
                counter: 0
            }
        },
        methods: {
            // 这两个方法都操控的同一个counter,而且现在是在组件里面,还需要向父组件传递数据
            // 当子组件需要向父组件传递数据的时候,就要用到自定义事件,v-on除了可以监听事件之外,还可以用于组件之间的自定义事件

            // 触发事件,将消息传递给父组件
            //$emit()第一个参数是自定义事件的名称,第二个参数是向自定义事件传递的参数
            handleIncrease: function () {
                this.counter++;
                this.$emit('increase', this.counter)  //儿子向父亲传递数据
            },
            handleReduce: function () {
                this.counter--;
                this.$emit('reduce', this.counter)
            }
        }
    });
    var app = new Vue({
        el: "#app",
        data: {
            total: 0  //双向数据绑定
        },
        methods: {
            handleGetTotal: function (total) {
                this.total = total;
            }
        }
    })
</script>
</body>
</html>

 

2.使用v-model

还有另外一个更加快捷的方法,使用v-model做双向的数据绑定。
在自定义的标签里面有counter这个属性,使用v-model直接绑定total这个属性。
不过这里面使用的是input这个特殊事件名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <p>{{ total }}</p>
        <!--我们还可以在自定义组件上使用v-model-->
        <!--这样将total直接与组件绑定到i此绑定在一次-->
        <my-component v-model="total"></my-component>
    </div>
    <script>
        Vue.component('my-component',{
            template: '<button @click="handleIncrease">+1</button>',
            data: function () {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function () {
                    this.counter++;
                    //这次使用的是特殊的input
                    this.$emit('input',this.counter)
                }
            }
        });
        var app = new Vue({
            el: "#app",
            data: {
                total: 0,
            }
        })

    </script>
</body>
</html>

v-model还可以用来创建自定义的表单输入组件,进行数据双向绑定。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <p>总数: {{ total }}</p>
        <my-component v-model="total"></my-component>  <!--直接绑定到total上面-->
        <button @click="handleReduce">-1</button>
        <!--这其实是一个三方通信-->
        <!--v-modelp->my-component-->
        <!--my-component->button-->
    </div>
    <script>
        Vue.component('my-component',{
            props:['value'],
            template: '<input :value="value" @input="updateValue">',  //input标签绑定到这个函数,组件中接收value这个值
            methods: {
                updateValue: function (event) {  //event代表当前操作的这个对象
                    this.$emit('input',event.target.value);  //将值传回去
                }
        }
        });
        var app = new Vue({
            el: "#app",
            data: {
                total: 0,
            },
            methods: {
                handleReduce: function () {
                    this.total--;
                }
            }
        })
    </script>
</body>
</html>

实现这样一个具有双向绑定的v-model组件要满足下面两个要求:
  接收一个value属性
  在有新的value时触发input事件

 

3.非父子组件通信

在vue1中,$dispatch用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在vue实例的event选项内接收,
还提供了$broadcast用于上级向下级广播事件。
这两种方法一旦发出事件之后,任何组件都可以接收,就近原则而且会在第一次接收到后停止冒泡,除非返回true。
在vue2中这两个事件被废弃,因为基于组件树结构的事件流的方式难以让人理解,并且在扩展的过程中会变得越来越脆弱。
并且不能解决兄弟组件之间的通信问题。

在vue2中使用一个空的Vue实例作为中央事件总线,也就是一个中介,专门用来传达消息。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        {{ message }}
        <my-component></my-component>
    </div>
    <script>
        var bus = new Vue();  //创建一个空的Vue实例,里面并没有任何内容
        Vue.component('my-component',{  //定义组建
            template: '<button @click="handleEvent">传递事件</button>',
            methods: {
                handleEvent: function () {
                    bus.$emit('on-message','来自组件my-component的内容')  //将事件发送到bus
                }
            }
        });
        var app = new Vue({
            el: "#app",
            data: {
                message: '',
            },
            mounted: function () {  //在初始化app的过程中,监听来自bus的消息
                var _this = this;
                bus.$on('on-message',function (msg) {  //在初始化的过程中,监听来自bus实例的事件
                    _this.message = msg
                })
            }

        })
    </script>
</body>
</html>

首先创建一个名为bus的空Vue实例,里面没有任何内容。
然后全局定义组件my-component,最后创建Vue实例app,在app初始化时,
也就是生命周期钩子函数里面监听来自bus的事件on-message,
而在组件my-component中点击按钮会通过bus把事件on-message发出去,
此时app就会就收来自bus的事件,进而在回调里完成自己的业务逻辑。

这种方法巧妙的轻量的实现了任何组件之间的通讯,包框父子\兄弟\跨级,
如果深入使用,可以扩展bus实例,给他添加data、methods、computed等选项,
在业务中,经常需要共享一下信息,比如用户登录星系、授权token,只需要让bus获取一次机型了。

 

4.父链

在子组件中,使用this.$parent可以直接访问该组件的父实例或组件,
父组件也可以通过this.$children访问他的所有子组件,而且可以递归向上或向下无限访问。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        {{ message }}
        <my-component></my-component>
    </div>
    <script>
        Vue.component('my-component',{
            template: "<button @click='handleChange'>通过父链直接修改数据</button>",
            data: function () {
                return {
                    message: '来自组件的消息'
                }
            },
            methods: {
                handleChange: function () {
                    this.$parent.message = this.message
                }
            }
        });
        var app = new Vue({
            el: "#app",
            data: {
                message: '老子的老大'
            }
        })
    </script>
</body>
</html>

尽管Vue允许这样做,但是并不建议这样做,
子组件不应该过多的依赖父组件,更不应该主动去修改它的数据。

 

5.子组件索引

当子组件比较多的时候,通过this.$children来遍历组件比较麻烦。
vue提供了子组件索引的方法,用特殊的属性ref来为子组件指定一个索引名称。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
    <div id="app">
        <p>{{ preMessage }}</p>
        <button @click="handleRef">通过ref过去子组件实例</button>
        <my-component ref="comA"></my-component>  <!--指定索引标签-->
    </div>
    <script>
        Vue.component('my-component',{
            template: "<div></div>",
            data: function () {
                return {
                    message: '子组件的内容'
                }
            }
        });
        var app = new Vue({
            el: "#app",
            data: {
                preMessage: '父组件内容'
            },
            methods: {
                handleRef: function () {
                    this.preMessage = this.$refs.comA.message  //访问指定的子组件
                }
            }
        })
    </script>

</body>
</html>

效果:

 

posted @ 2020-04-12 22:58  明王不动心  阅读(337)  评论(1编辑  收藏  举报