当我们在封装vue组件时,难免会用到插槽slot来进行子组件的嵌套,那么,当slot插槽处的内容组件需要和父组件进行通信时,怎么办呢?场景代码如下:
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
| <template> <div> <slot></slot> </div> </template> <script> export default { methods: { }, } </script>
<template> <div> <input @input="onInput"> </div> </template> <script> export default { methods: { onInput(){} }, } </script>
|
使用的时候,如下:
1 2 3
| <parent> <child></child> </parent>
|
首先,我们都知道,在vue中,子组件向父组件进行通信传值时,是借用在子组件中派发自定义事件,然后在父组件中的子组件引用之处进行监听自定义事件,如下(@test=”demo”):
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
| <template> <div> <child @test="demo"></child> </div> </template> <script> import child from 'child.vue' export default { components:{child}, methods: { demo(val) { console.log(val) } }, } </script>
<template> <div> <input @input="onInput"> </div> </template> <script> export default { methods: { onInput(e) { this.$emit('test', e.target.value) } }, } </script>
|
但是,插槽slot是无法进行自定义事件的监听的,换言之,此种写法会报错:
1
| <slot @test="fn"></slot>
|
那么,怎么解决这种问题呢?其实方法有很多,笔者这里说下自己的解决方案:
我们可以在子组件中调用父组件去触发一个自定义事件,然后在父组件自己的生命周期函数中对这个自定义事件进行监听,code如下:
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
| <template> <div> <slot></slot> </div> </template> <script> export default { mounted(){ this.$on('test', this.fn) }, methods: { fn(val){ console.log(val) } }, } </script>
<template> <div> <input @input="onInput"> </div> </template> <script> export default { methods: { onInput(e){ this.$parent.$emit("test", e.target.value) } }, } </script>
|
在这里说白了,就是要找到包含slot的组件的Vue实例的引用,然后让这个组件的实例自己去触发那个约定好的自定义事件,然后由于它自己在自己的Vue实例初始化的时候已经对这个自定义事件进行了监听$on,所以当它自己的实例以$emit的形式触发该自定义事件的时候,所监听的事件就会触发相应的回调,从而达到通信的目的。
要找到此实例也很容易,像上述利用$parent($children)的形式只是其中的一种,我们再来看一下另外一种形式($refs):
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 39
| <template> <div> <slot></slot> </div> </template> <script> export default { mounted(){ this.$on('test', this.fn) }, methods: { fn(val){ console.log(val) } }, } </script>
<template> <div> <parent ref='parent'> <button @click='clickFn'>点我</button> </parent> </div> </template> <script> import Parent from './parent.vue' export default { components: {parent}, methods: { clickFn() { this.$refs.parent.$emit('test', 666) } }, } </script>
|
还有一种方式是利用在Vue的原型上挂载一个Vue实例,即eventBus,利用Vue实例的eventBus进行跨组件间的通信;再更进一步也可以利用vuex,但是个人觉得无此必要。
这样即可解决slot插槽与其父组件的通信问题了!
另符vue官方文档对于监听自定义事件的说明:vue中$on的说明