场景1: A组件引用了B组件,B组件引用了C组件,此时A组件和C组件就是祖孙级的关系,A和B或者B和C都是父子级的关系。父子级的组件的传参用prop和
parnet,都可以搞定。但是A和C之间的传参怎么办呢? 也是有很多方法滴!可以依据层级关系,;可以用vuex;可以用EventBus。
- 依据层级关系传参: A和B是父子组件,A先传给B,B再传给C。但是导致代码太过繁琐,如果B组件中要定义一堆props和methods
- 用vuex: 不得不说vuex简直太方便了,但是如果你的项目很小,整个项目就一个地方用到了祖孙级的传参,那引入vuex就显得很不划算了
- EventBus: 本人不怎么用,具体没研究过~
接下来介绍一种利用vue2.4.0新增的特性实现祖孙传参的方法:
inheritAttrs 文档

如果是第一次看到这个属性,看文档的介绍就有些不知所云,直接用代码尝试一下:
子组件代码child.vue:子组件中props里定义了name,name是父组件要传的
<template>
<div class="childContent">
my name is {{name}}, 测试inheritAttrs属性
</div>
</template>
<script>
export default{
data () {
return {}
},
props: ['name']
}
</script>
父组件代码index.vue:父组件在调用child的时候传了name,还传了一些其他的内容,age,university,quantity。
<template>
<div class="fatherContent">
<child
:name="myname"
age="20"
university="山西大学"
:quantity="num"
></child>
</div>
</template>
<script>
import child from './child.vue'
export default{
components: { child },
data () {
return {
myname: '南风NY',
num: 0
}
}
}
</script>
浏览器渲染结果:

可以看到子组件不要的属性age,university,quantity被作用在了子组件的根元素上,也就是class为childContent的div上,这就是文档中所说的 “默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。”
inheritAttrs默认为true,在子组件中设置inheritAttrs为false
子组件child.vue:
<template>
<div class="childContent">
my name is {{name}}, 测试inheritAttrs属性
</div>
</template>
<script>
export default{
inheritAttrs: false, // 默认为true, 现在设为false
data () {
return {}
},
props: ['name']
}
</script>
浏览器渲染结果:

接下来再分析一下文档中的最后一句
通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上。
结合上下文解释:虽然inheritAttrs设置为false,不会将父组件传的多余的属性作用到子组件上,但是在子组件中通过$attrs可以访问这些属性
确实是,在子组件中我打印了$attrs, 包含除了name(props包含)外的其他属性

$attrs 文档
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
再一次不知所云~~ 吗?
直白的解释: attrs里;并且可以通过 v-bind="$attrs" 传入内部组件。
代码如下:
父组件index.vue:引用子组件child,传入name,age,university,quantity
<template>
<div class="fatherContent">
<child
:name="myname"
age="20"
university="山西大学"
:quantity="num"
></child>
</div>
</template>
<script>
import child from './child.vue'
export default{
components: { child },
data () {
return {
myname: '南风NY',
num: 0
}
}
}
</script>
子组件child.vue: 无需定义大量props,用v-bind绑定$attrs到grandson组件上
<template>
<div class="childContent">
测试inheritAttrs和绑定$attrs
<grandson v-bind="$attrs"></grandson>
</div>
</template>
<script>
import grandson from './grandson.vue'
export default{
inheritAttrs: false, // 默认为true, 现在设为false
components: { grandson },
created () {
console.log('$attrs:', this.$attrs)
}
}
</script>
孙组件grandson.vue:定义props,直接用这些属性
<template>
<div>
my name is {{name}},
I am {{age}},
university is {{university}}
<el-button type="primary">点赞 + {{quantity}}</el-button>
</div>
</template>
<script>
export default{
props: ['name', 'age', 'university','quantity'],
methods: {}
}
</script>
浏览器渲染结果:

这样就实现了父组件向孙组件传参,是不是很简单~
那么问题来了,grandson组件如何向父组件传参呢? grandson组件如何改变父组件传过来的值呢?
莫慌!官方文档很人性化的提供了vm.$listeners,也是2.4.0新增的
$listeners 文档
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
直白的解释:也是用在子组件中的一个属性,是一个对象,包含子组件中未emit但是父组件监听了的不含 .native 修饰器的事件监听器。 好吧!可能不是很直白!直接用代码解释吧:
父组件代码index.vue: 监听了addQuantity
<template>
<div class="fatherContent">
<child
:name="myname"
age="20"
university="山西大学"
:quantity="num"
@addQuantity="addQuantity"
></child>
</div>
</template>
<script>
import child from './child.vue'
export default{
components: { child },
data () {
return {
myname: '南风NY',
num: 0
}
},
methods: {
addQuantity () {
this.num++
}
}
}
</script>
子组件代码child.vue: 打印$listeners
<template>
<div class="childContent">
测试inheritAttrs和绑定$attrs
<grandson v-bind="$attrs"></grandson>
</div>
</template>
<script>
import grandson from './grandson.vue'
export default{
inheritAttrs: false, // 默认为true, 现在设为false
components: { grandson },
created () {
console.log('$attrs:', this.$attrs)
console.log('$listeners', this.$listeners)
}
}
</script>
打印结果:

通过 v-on="$listeners" 传入内部组件
父组件代码index.vue: 监听addQuantity
<template>
<div class="fatherContent">
<child
:name="myname"
age="20"
university="山西大学"
:quantity="quantity"
@addQuantity="addQuantity"
></child>
</div>
</template>
<script>
import child from './child.vue'
export default{
components: { child },
data () {
return {
myname: '南风NY',
quantity: 0
}
},
methods: {
addQuantity (haslike) {
this.quantity += haslike
}
}
}
</script>
子组件child.vue: 通过 v-on="$listeners" 传入到孙组件
<template>
<div class="childContent">
测试inheritAttrs和绑定$attrs
<grandson v-bind="$attrs" v-on="$listeners"></grandson>
</div>
</template>
<script>
import grandson from './grandson.vue'
export default{
inheritAttrs: false, // 默认为true, 现在设为false
components: { grandson },
created () {
console.log('$attrs:', this.$attrs)
console.log('$listeners', this.$listeners)
}
}
</script>
孙组件grandson.vue: 触发父组件监听的addQuantity,并传入自己的值haslike
<template>
<div>
my name is {{name}},
I am {{age}},
university is {{university}} <br/>
我的掘力值 {{quantity}} <br/>
<el-button type="primary" @click="add">点赞</el-button>
</div>
</template>
<script>
export default{
props: ['name', 'age', 'university','quantity'],
data () {
return {
haslike: 10,
}
},
methods: {
add () {
this.$emit('addQuantity', this.haslike)
}
}
}
</script>
渲染效果: 点赞一次掘力值加10

————————------------------分割线———————————————
祖孙组件传参完美实现啦!