前言
今天接手了另一位同事的代码改一个需求时,发现这个需求涉及到的组件层层嵌套,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>
效果如下:

