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之中,同样,刚才已经提到过,如果子组件接收了父组件的数据,那么在孙组件就会看不到数据,正是这个原因