Vue小技巧之属性、事件传递:v-bind="$props",v-bind="$attrs",v-on="$listeners"

前言

今天接手了另一位同事的代码改一个需求时,发现这个需求涉及到的组件层层嵌套,props也得层层传递,如图所示:

从A组件传递的props需要分别传递到第二第三层组件中使用。如果我改这个需求得添加一个属性,就意味着我需要在ABCDE…这几个组件调用的地方分别加上这个属性。想想就非常不优雅:不仅费时,也不利于以后的拓展和维护。那么有什么方法可以优化呢?

正好前段时间写Vue3UI轮子button组件的时候,用到了v-bind="$attrs"来将属性和事件绑定给子组件。于是进行了一番探索(搜博客+查阅文档),发现vue2中也有类似的操作,分别是通过v-bind="$props",v-bind="$attrs"传递属性,通过v-on="$listeners"传递事件。

使用介绍

v-bind=”$props”

  • vm.$props: 当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问

  • v-bind="$props"将父组件的所有props向下传递给它的子组件,子组件需要在其props:{} 中定义要接受的props

v-bind=”$attrs”

  • vm.$attrs :包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外)
  • v-bind="$attrs": 将调用组件时,组件标签上绑定的非props特性(class和style除外)向下传递

v-on=”$listeners”

  • vm.$listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件
  • v-on="$listeners":将父组件标签上的自定义事件向下传递,其子组件可以直接通过this.$emit(eventName)的方式调用

注:Vue3中移除了$listeners,而将事件存在了vm.$attrs

举个栗子

  • index.vue

    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
    <template>
    <div>
    <Parent test="非props属性" :name="name" :age="age" v-on:start1="fn1" @start2="fn2">
    </Parent>
    </div>
    </template>

    <script>
    import Parent from './parent.vue';
    export default {
    components: {
    Parent
    },
    data() {
    return {
    name: '爷爷给爸爸的值',
    age: '18'
    };
    },
    methods: {
    fn1() {
    console.log('这是从爷爷传来的方法1');
    },
    fn2() {
    console.log('这是从爷爷传来的方法2');
    }
    }
    }
    </script>
  • parent.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <template>
    <div>
    <h3>父组件</h3>
    <div>非props属性($attrs): {{$attrs}}</div>
    <child v-on="$listeners" v-bind="$props"></child>
    </div>
    </template>

    <script>
    import Child from './child.vue';
    export default {
    inheritAttrs: false,
    components: {
    Child
    },
    props: ['name', 'age'],
    mounted() {
    this.$emit('start1');
    }
    }
    </script>
  • child.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <template>
    <div>
    <h3>子组件</h3>
    <div>爸爸传递过来的name: {{name}}</div>
    <div>爸爸传递过来的age: {{age}}</div>
    </div>
    </template>

    <script>
    export default {
    props: ['name', 'age'],
    mounted() {
    this.$emit('start2');
    console.log(this.$listeners, '儿子的事件');
    }
    }
    </script>

效果如下:

参考

https://juejin.cn/post/6844903848050589704#heading-4

https://cn.vuejs.org/v2/api/?#v-on