v-bind="$attrs"
作用:主要用于组件之间的隔代传值,是将调用组件时的组件标签上绑定的非props的属性(class、style除外)向下传递。
使用了v-bind="$attrs"
的组件,可以使用从上级组件中获取的值,并且它的下级组件直接下级组件即便没有用v-bind="$attrs"
绑定,也可以获取到值。
在子组件中应该添加inheritAttrs:false
,避免父作用域的不被认作为props的特性绑定应用到子组件的根元素上,导致在子组件中无法使用vm.$attrs
对象获取到非props特性值
**eg:**有父组件A,子组件B,孙组件C,在A组件中传值给C,在C组件中用prop接收
// 层级关系
<App>
<TheOuter>
<TheMiddle>
<TheInner>
</TheInner>
</TheMiddle>
</TheOuter>
</App>
|---App.vue
<template>
<div id="app">
<TheOuter id="1" name="zxx" age="23" />
</div>
</template>
|---App.vue---TheOuter.vue
<template>
<div>
<TheMiddle v-bind="$attrs" gender="man"></TheMiddle>
</div>
</template>
// ----------------------------------
mounted(){
// console.log(this.$attrs['id']); // 1
// console.log(this.$attrs['name']); // jack
// console.log(this.$attrs['age']); // 21
}
|---App.vue---TheOuter.vue---TheMiddle.vue
<template>
<div>
<TheInner v-bind="$attrs"></TheInner>
</div>
</template>
// ----------------------------------
mounted(){
// prop中变量的使用
// console.log(this.$attrs['id']); // 1
// console.log(this.$attrs['name']); // jack
// console.log(this.$attrs['age']); // 21
// console.log(this.$attrs['gender']); // man
}
|---App.veu---TheOuter.vue---TheMiddle.vue---TheInner.vue
<template>
<div>
<div>hello啊</div>
</div>
</template>
// ----------------------------------
mounted(){
// prop中变量的使用
// console.log(this.$attrs['id']); // 1
// console.log(this.$attrs['name']); // jack
// console.log(this.$attrs['age']); // 21
// console.log(this.$attrs['gender']); // man
}
vue2中的v-bind="$attrs"
与vue3中的v-bind="$attrs"
区别
-
vue2中
$listeners
是单独存在的,vue3中$listeners
不存在了 -
vue2
$attrs
不包含class和style属性,vue3中$attrs
包含class和style属性
vue2中的v-bind="$attrs"
在vue2中,attrs里面包含了上层组件传递的所有数据(除class和style),当一个组件声明了prop时,attrs里面包含了除去 prop里面的数据剩下的数据,可以结合inheritAttrs: false
,可以将传递下来的数据应用于其他元素
vue3中的v-bind="$attrs"
-
vue3中
$attrs
包含class和style属性 -
<script setup>
和普通的<script>
可以一起使用 -
无法在
<script setup>
中的声明选项中去使用inheritAttrs
或者插件的自定义选项<script lang="ts"> export default{ inheritAttrs: false, customOptions:{} } </script> <script lang="ts" setup> </script>
默认是渲染到子组件的根标签上,通过
inheritAttrs:false
属性让属性不渲染到根标签上,在子组件的任意标签上添加v-bind=$attrs
可以让传递过来的属性渲染到任意标签上。使用defineProps
接收后会相应减少对应属性。
inheritAttrs
vue官网对于inheritAttrs的属性解释:默认情况下父作用域的不被认作为props的attribute绑定 (attribute bindings) 将会“回退”且认作为普通的HTML attribute应用到子组件的根标签上。
v-bind="$props"
作用:主要用于组件之间隔代传值。
可以将父组件的所有props下发给它的子组件,子组件需要在其props中定义接收的props。
v-on="$listeners"
$listeners
包含了父作用域中的(不含.native
修饰符的)v-on
事件监听器。他可以通过v-on="$listeners"
传入内部组件–在创建更高层次的组件时非常有用。
vue2中$listeners
是单独存在的,vue3中$listeners
被合并到了$attrs
中。
理解:
- 因为
$listeners
可以接收父组件中(不含.native
修饰符的)v-on
事件监听器,在进行事件传递的时候非常有用。 - 对组件进行二次封装时不可能将组件的所有内置组件都抛出来,可以用
$listeners
进行事件传递。
vue2中的$listeners
第一种
作用:主要用于底层组件向高层组件传递信息
**eg:**有父组件A,子组件B,孙组件C,从C组件传递消息给A组件
// 层级关系
<App>
<A>
<B>
<C>
</C>
</B>
</A>
</App>
|---App.vue---A.vue---B.vue---C.vue
<template>
<div @click="hanleClick">C组件</div>
</template>
<script>
const hanleClick = () => {
this.$emit("Msg",'123')
}
</script>
|---App.vue---A.vue---B.vue
<template>
<C v-on="$listeners" />
</template>
|---App.vue---A.vue
<template>
<B @Msg="msg" />
</template>
<script>
// 接口
interface Msg<T>{
(a:T):any
}
// 类型别名
type Msg<T> = (a:T) => any;
const msg:Msg = (val:T) => {
console.log(val)
}
- 可以理解成穿透,多层组件监听。
- 用于多层级组件之间的通信。
- 高层级向低层级传值
v-bind="$attrs"
。 - 低层级向高层级传值
v-on="$listeners"
。
第二种(仅限于vue2)
作用:父组件调用子组件方法,给挂载子组件加方法,然后在子组件需要加的方法加v-on="$listeners"
,就可以访问到子组件添加的对应方法
// 父组件
<template>
<Search @change="handleChangeClicks"></Search>
</template>
<script>
methods: {
handleChangeClicks(val) {
if (val.length) return;
this.getPurchaseOrderList();
},
}
</script>
// 子组件
<template>
<b-form-input
v-model="value"
v-on="$listeners"
type="search"
:placeholder="placeholder"
></b-form-input>
</template>
vue3中的$listeners
,v-bind="$attrs"
会接收属性和事件
//子组件
<template>
<el-button text @click="dialogTableVisible = true"> 打开 </el-button>
<el-dialog width="600px" v-bind="$attrs" v-model="dialogTableVisible" title="我是标题">
<div>我值弹窗中的内容</div>
</el-dialog>
</template>
<script lang="ts">
export default {
inheritAttrs: false,
}
</script>
<script lang="ts" setup>
import { ref } from 'vue'
const dialogTableVisible = ref(false)
</script>
ps:我们没有向上抛出任何事件。
但是父组件可以调用 Element Plus 中对话框中的内置方法。
但是父页面中可以 注册 Element Plus 中对话框中的内置方法。
// 父组件
<template>
<div class="father">
<TestCom @close="closeHandler" :before-close="beforeclose" title="父组件给的标题" aa="我是aa" bb="我是bb"></TestCom>
</div>
</template>
<script setup lang="ts">
import { ElMessageBox } from 'element-plus'
import TestCom from "../../components/TestCom.vue"
// Dialog 关闭的回调
const closeHandler = () => {
console.log('Dialog 关闭的回调')
}
/*
before - close 只会在用户点击关闭按钮或者对话框的遮罩区域时被调用。
如果你在 footer 具名插槽里添加了用于关闭 Dialog 的按钮,那么可以在按钮的点击回调函数里加入 before - close 的相关逻辑。
关闭前的回调,会暂停 Dialog 的关闭. 回调函数内执行 done 参数方法的时候才是真正关闭对话框的时候.
*/
const beforeclose = (done: () => void) => {
ElMessageBox.confirm('Are you sure to close this dialog?')
.then(() => {
console.log('用户点击了确定')
done()
})
.catch(() => {
console.log('用户点击了取消')
})
}
</script>