透传的attribute
介绍$arrts之前先讲一下“透传的attribute”,“透传的attribute”指的的是传递给一个组件,却没有被组件声明为prop和emit的attribute,常见的就是style,class。
以一个<MyButton>组件为例:
<!-- Mybutton -->
<template>
<button>click me</button>
</template>
<script>
export default {
props:{
foo:String
},
}
</script>
当你在父组件这样调用:
<my-button
class="button"
style="margin-top:20px"
:foo="foo"
:bar="bar"
></my-button>
这时class、style、bar会自动被添加到根元素上,渲染出来的dom就是这样:
<button bar="bar" class="button" style="margin-top: 20px;">
click me
</button>
禁用Attribute继承
如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false
来禁用Attribute继承:
<!-- Mybutton -->
<template>
<button>click me</button>
</template>
<script>
export default {
inheritAttrs: false,//新增
props:{
foo:String
},
}
</script>
设置禁用Attribute继承后,vue2和vue3有所区别。vue2渲染的dom依然会保留class和style,只把没有在组件的prop中声明的attribute禁用了:
<button class="button" style="margin-top: 20px;">click me</button>
可以看到原来在dom中的bar="bar"消失了。而vue3渲染的dom则会禁用包括class、style在内的attribute,渲染出来的dom为:
<button>click me</button>
$attrs
当父组件传递的attribute 需要应用在根节点以外的其他元素上时,可以通过$attrs访问到。
需要注意的是,vue2和vue3中的$attrs不一样,vue3中的$attrs是一个代理对象,包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等。
而vue2中的$attrs只包含了组件声明的prop之外的属性,这其中不包括class,style。还是以<Mybutton>为例:
//Mybutton.vue
<template>
<button>click me</button>
</template>
<script>
export default {
props:{
foo:String
},
created(){
console.log(this.$arrts)//重点看这里的打印结果
}
}
</script>
//parent.vue
<template>
<div>
<my-button
class="button"
style="margin-top:20px"
:foo="foo"
:bar="bar"
@changeMsgB="changeMsgB"
@changeMsgc="changeMsgc"
></my-button>
</div>
</template>
<script>
import myButton from "../components/myButton.vue"
export default {
components:{
myButton
},
data(){
return {
foo:"foo",
bar:"bar",
}
},
methods:{
changeMsgB(val){},
changeMsgc(val){},
}
}
</script>
以上这段代码在vue2中运行的打印结果为:
在vue3中运行的打印结果为:
vue2中也可以通过$listeners在组件内访问到父组件传递的v-on,而在vue3中则把$listeners废弃掉了,把除组件所声明的 props 和 emits 之外的所有其他 attribute都整合到了$attrs内
$attrs的组件通信
$attrs 常被用来进行祖先组件和子组件之间通信
vue2中通过$attrs和$listeners配合进行进行祖先组件和子组件之间通信,假设有三个组件A、B、C,组件之间的关系A>B>C,现在A组件需要向C组件传递数据,方式如下:
A组件:
<template>
<div>
<component-b
:foo="foo"
:bar="bar"
@changeMsgc="changeMsgc"
></component-b>
</div>
</template>
<script>
import componentB from "../components/B.vue"
export default {
components:{
componentB
},
data(){
return {
foo:"foo",
bar:"bar",
}
},
methods:{
changeMsgc(val){
this.bar=val
},
}
}
</script>
B组件:
<template>
<div>
<span>b组件 foo:{{foo}}</span>
<component-c v-bind="$attrs" v-on="$listeners"></component-c>
</div>
</template>
<script>
import componentC from "../components/C.vue"
export default {
inheritAttrs: false,
components:{componentC},
props:{
foo:String
}
}
</script>
C组件:
<template>
<div>
<span>c组件 bar:{{bar}}</span>
<button @click="handleClick">update</button>
</div>
</template>
<script>
export default {
props:{
bar:String
},
methods:{
handleClick(){
this.$emit('changeMsgc','update')
}
}
}
</script>
vue3的组件传值
A组件:
<template>
<div>
<component-b
:foo="foo"
:bar="bar"
@changeMsgc="changeMsgc"
></component-b>
</div>
</template>
<script setup>
import componentB from "../../components/B.vue"
import {ref} from "vue"
const foo=ref('foo')
const bar=ref('bar')
const changeMsgc=(val)=>{bar.value=val}
</script>
B组件:
<template>
<div>
<span>b组件 foo:{{foo}}</span>
<component-c v-bind="$attrs"></component-c>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import componentC from "../components/C.vue"
export default defineComponent({
inheritAttrs: false,
components:{componentC},
props:{
foo:String
}
})
</script>
C组件:
<template>
<div>
<span>c组件 bar:{{bar}}</span>
<button @click="handleClick">update</button>
</div>
</template>
<script>
import {defineComponent} from "vue"
export default defineComponent({
props:{
bar:String
},
emits:['changeMsgc'],
setup(props,{emit}){
return {
handleClick(){
emit('changeMsgc','update')
}
}
}
})
</script>
最终效果: