Vue组件通信之$attrs、$listeners


前言

vue通信手段有很多种,props/emitvuexevent busprovide/inject等。还有一种通信方式,那就是 $attrs$listeners,之前早就听说这两个api,趁着有空来补补。这种方式挺优雅,使用起来也不赖。下面例子都会通过父、子、孙子,三者的关系来说明使用方式。

img

$attrs

官方解释

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (classstyle 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

我的理解

接收除了props声明外的所有绑定属性(class、style除外)

图解:

img

由于child.vue 在 props 中声明了 name 属性,$attrs 中只有agegender两个属性,输出结果为:

{ age: "20", gender: "man" }

img
上面打印结果为:

{height: '80', name: 'joe', gender: 'man'}//会把child页面的值,一起传过来

注:但若是child.vue页面有个prop:[“name”],则name不会传到grandson.vue页面中去,即:{height: '80', gender: 'man'},如下:

8

另外可以在 grandson.vue 上通过 v-bind="$attrs",可以将属性继续向下传递,让 grandson.vue 也能访问到父组件的属性,这在传递多个属性时会显得很便捷,而不用一条条的进行绑定。

如果想要添加其他属性,可继续绑定属性。但要注意的是,继续绑定的属性和 $attrs 中的属性有重复时,继续绑定的属性优先级会更高。

9
打印结果为:

{height: '80', name: 'xth', gender: 'man'}//会把child页面的值,一起传过来,同时会以继续绑定的属性为最高优先级

$listeners

官方解释:

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

我的理解:

接收除了带有.native事件修饰符的所有事件监听器

图解:

img

parent.vue 中对 child.vue 绑定了两个事件,带有.nativeclick 事件和一个自定义事件,所以在 child.vue 中,输出 $listeners 的结果为:

{ customEvent: fn }

img

attrs 属性一样,可以通过 v-on="$listeners",将事件监听器继续向下传递,让 grandson.vue 访问到事件,且可以使用 $emit 触发 parent.vue 的函数。

如果想要添加其他事件监听器,可继续绑定事件。但要注意的是,继续绑定的事件和 $listeners 中的事件有重复时,不会被覆盖。当 grandson.vue 触发 customEvent 时,child.vue 和 parent.vue 的事件都会被触发,触发顺序类似于冒泡,先到 child.vue 再到 parent.vue。

以上转自:Vue - 组件通信之$attrs、$listeners

代码示例

1.$attrs 示例

<div id="app">
    根组件
    <hr>
    <father :msg="msg" :rootName="name" :rootDetail="infor"></father>
</div>
<template id="father">
    <div>父组件---{{msg}}
        <hr>
        <son v-on="$listeners" v-bind="$attrs"></son>
    </div>
</template>

<template id="son">
    <div >子组件
    </div>
</template>
<script>
    // 子组件
    let son = {
        template: '#son',
        created() {
            console.log(this.$attrs, "子组件,接受root的非prop的数据")
        },
    }

    // 父组件
    let father = {
        template: '#father',
        components: {
            son
        },
        props: ["msg"],
        created() {
            console.log(this.$attrs, "父组件,接受root的非prop的数据")
        },
    }

    let vm = new Vue({
        el: '#app',
        components: {
            father
        },
        data() {
            return {
                msg: "根结点的数据",
                name: "root",
                infor: {
                    name: "xth",
                    age: 18,
                    address: "app根部"
                }
            }
        },
    })
</script>

img

2.$listeners 示例

<div id="app">
    根组件
    <hr>
    <father @cli1="click1" @cli2="click2" @cli3.native="click3"></father>
</div>
<template id="father">
    <div v-on="$listeners">父组件
        <button @click="$listeners.cli1">获取root方法cli1</button><br>
        <hr>
        <son v-on="$listeners"></son>
    </div>
</template>

<template id="son">
    <div >子组件
        <button @click="$listeners.cli2">获取root方法cli2</button>
    </div>
</template>
<script>
    // 子组件
    let son = {
        template: '#son',
        created() {
            console.log(this.$listeners, "子组件") // 包含父级所有绑定的方法
        },
    }

    // 父组件
    let father = {
        template: '#father',
        components: {
            son
        },
        created() {
            console.log(this.$listeners, "父组件,接受父级所有绑定的方法") // 包含父级所有绑定的方法
        },
    }

    let vm = new Vue({
        el: '#app',
        components: {
            father
        },
        methods: {
            click1() {
                console.log('点击事件cli1')
            },
            click2() {
                console.log('点击事件cli2')
            },
        },

    })
</script>

img

3.$attrs 、$listeners 、 prop 、 $emit 示例

<div id="app">
    根组件
    <hr>
    <father :msg="msg" :rootName="name" :rootDetail="infor" @cli1="click1" @cli2="click2" @cli3.native="click3"></father>
</div>
<template id="father">
    <div v-on="$listeners">父组件---{{msg}}
        <button @click="$listeners.cli1">获取root方法cli1</button><br>
        <button @click="getRootMethod">获取root方法</button>
        <hr>
        <son v-on="$listeners" v-bind="$attrs" @fathermethod="fatherMethod"></son>
    </div>
</template>

<template id="son">
    <div >子组件
        <button @click="$listeners.cli2">获取root方法cli2</button>
        <button @click="getFatherMethod">获取father方法</button>
    </div>
</template>
<script>
    // 子组件
    let son = {
        template: '#son',
        created() {
            console.log(this.$attrs, "子组件,接受root的非prop的数据")
            console.log(this.$listeners, "子组件") // 包含父级所有绑定的方法
        },
        methods: {
            getFatherMethod() {
                this.$emit('fathermethod');
            }
        }
    }

    // 父组件
    let father = {
        template: '#father',
        components: {
            son
        },
        props: ["msg"],
        created() {
            console.log(this.$attrs, "父组件,接受root的非prop的数据")
            console.log(this.$listeners, "父组件,接受父级所有绑定的方法") // 包含父级所有绑定的方法
        },
        methods: {
            getRootMethod() {
                this.$parent.rootMethod();
            },
            fatherMethod() {
                console.log('子组件的getFatherMethod方法点了我');
            },
        }
    }

    let vm = new Vue({
        el: '#app',
        components: {
            father
        },
        data() {
            return {
                msg: "根结点的数据",
                name: "root",
                infor: {
                    name: "xth",
                    age: 18,
                    address: "app根部"
                }

            }
        },
        methods: {
            click1() {
                console.log('点击事件cli1')
            },
            click2() {
                console.log('点击事件cli2')
            },
            click3() {
                console.log('点击事件cli3')
            },
            rootMethod() {
                console.log('父组件的getRootMethod方法点了我');
            },
        },

    })
</script>

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录