1.概述:$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖-》孙)
2.具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。
注意:$attrs会自动排除props中声明的属性(可以认为声明过的props被子组件自己消费了)
首先,创建父-子-孙之间的组件关系
我们首先来看父子之间的传值:
我们都知道,父传子传值的口诀是:父在子的属性上书写属性名=属性值,子组件使用defineProps接收。
那么我们的父组件的代码如下:【组件关系:爷爷:index.vue,子:brother.vue,孙:grandSon.vue】
index.vue
<template> <div> <el-card ref="myButton" @click="handlerClick">Click Me<el-card @click="clickSpan">点我</el-card><Children ref="childRef" :fatherToSon="handlerClick"></Children><!-- @myHandler="handlerClick" --><Brother v-model:aaa="name" age="20" :happy="happy" :happyPlus="happyPLus" test="测试数据" v-bind="{x:100,y:200}" /><!-- <el-tag type="danger" size="normal" effect="dark" closable @close="">{{ name }}</el-tag --><el-badge :value="name" :max="99" :is-dot="false" :hidden="false" type="primary"><el-button size="small">cet</el-button></el-badge><br />爷爷的:{{ happy }}</el-card> </div> </template> <script lang="ts" setup> import { ref, onMounted, nextTick } from 'vue'; import { ElButton } from 'element-plus'; // 确保你已经正确引入了 Element Plus import Children from './child.vue' import Brother from './brother.vue'const childRef = ref(null); const name = ref('tomCat')
let happy=ref('哈哈')
const happyPLus=()=>{happy.value+='!'
}const myButton = ref(null); const handlerClick=(value:any)=>{console.log("父组件被点击了,子组件传递过来的值为",value);console.log(childRef.value);console.log(childRef.value?.SonFun());console.log(childRef.value?.sonValue);}const clickSpan=()=>{console.log("span被点击了");}// onMounted(() => { // // 访问 el-button 组件实例 // console.log(myButton.value); // console.log(24,myButton.value.handleClick);// console.log(25,ref);// // 打印所有属性和方法(注意:直接操作实例的属性和方法可能不是最佳实践) // console.log('Properties:', Object.keys(myButton.value.$props)); // console.log('Methods:', Object.keys(myButton.value.$options.methods)); // }); // 返回 ref 以便在模板中使用 </script>
可以看到父给子传递的数据,有静态的,也有动态的,也有使用v-bind=对象的这种方式来传递的,其实这种方式就是等价于:
v-bind="{x:100,y:200}" ==》 :x=100 :y=200
这样我们就可以看到对应的x和y也被传递过来了,然后我们的关注重点是祖孙传值,而不是父子传值,所以这个brother组件就是一个桥梁,其作用就是不使用父组件的值,直接将父组件传递的值使用$attrs直接传递给孙组件。
brother.vue
<template><div>自定义组件实现v-model<!-- <input type="text" :value="aaa" @input="inputChange"/><h3>age:{{ age }}</h3><h3>其他:{{ $attrs }}</h3> --><GrandSon v-bind="$attrs"></GrandSon></div>
</template><script setup lang="ts">
import {ref,reactive} from 'vue'
import GrandSon from './grandSon.vue';
// const emit =defineEmits(['update:aaa'])// defineProps(['aaa','age'])// const inputChange=({target:{value}})=>{
// emit('update:aaa',value)// }
</script><style scoped></style>
这里我们需要注意的时,当父组件传递给孙组件的值被子组件接收后,如果子组件没有传递出去,那么孙组件就无法使用被子组件占用的值。
孙组件grandSon.vue
<template><div>this is grandson page
<h2>test:{{ test }}</h2>
<h2>age:{{ age }}</h2>
<h2>x:{{ x }}</h2>
<h2>y:{{ y }}</h2>
<h2>孙子的happy:{{ happy }}</h2><el-button type="primary" size="default" @click="happyPlus">修改爷爷的happy</el-button></div>
</template><script setup lang="ts">
import {ref,reactive} from 'vue'
defineProps(['test','age','x','y','happy','happyPlus'])</script><style scoped></style>
来到调试工具,我们就可以看到孙组件接受到了来自爷爷传递过来的值。
然后我们可能会有疑问,既然爷爷可以向孙组件传递数据,那么孙可不可以修改爷爷的数据呢,其实,原理和当时的props是一样的,这种方式虽然不能直接修改爷爷的值,但是可以让爷爷再传递一个方法给孙组件,然后孙组件调用这个方法,完成对爷爷的修改。比如传递过来的函数happyPLus
来到界面,我们点击按钮,就可以修改爷爷的数据
有几个比较细致的点,需要注意一下,就是当父组件给子组件传递数据的时候,子组件可以不全部接受父组件传递过来的数据,
可以看到,接受到的数据被放在props中,没接受的数据放到了attrs之中,同样,刚才已经提到过,如果子组件接收了父组件的数据,那么在孙组件就会看不到数据,正是这个原因