Vue3 透传 Attributes
- [1. Attributes 继承](#1. Attributes 继承)
-
- [1.1 单个根文件继承(直接继承 attribute 和 事件监听器)](#1.1 单个根文件继承(直接继承 attribute 和 事件监听器))
- [1.2 深层组件继承(一代单传,代代相传)](#1.2 深层组件继承(一代单传,代代相传))
- [2. 禁用根节点 Attributes 继承](#2. 禁用根节点 Attributes 继承)
-
- [2.1 使用方式(defineOptions( inheritAttrs: false}))](#2.1 使用方式(defineOptions( inheritAttrs: false})))
- [2.2 指定非根节点继承 Attributes(结合 v-bind="attrs)](#2.2 指定非根节点继承 Attributes(结合 v-bind="attrs))
- [3. 多根节点的 Attribute 继承(使用 v-bind="attrs" 指定一个继承)](#3. 多根节点的 Attribute 继承(使用 v-bind="attrs" 指定一个继承))
- [4. 在 JavaScript 中访问透传 Attributes(const attrs = useAttrs())](#4. 在 JavaScript 中访问透传 Attributes(const attrs = useAttrs()))
1. Attributes 继承
透传 attribute,指的是传递给子组件 attribute 或者 v-on 事件监听器(但是子组件却没有声明 props 或 emits)。最常见的例子就是 class、style 和 id。
1.1 单个根文件继承(直接继承 attribute 和 事件监听器)
子组件 Mybutton.vue:
javascript
<template>
<button class="btn" @click="handleClick">Click Me</button>
</template>
<script setup>
function handleClick() {
console.log('Button clicked!')
// 这里可以添加更多的逻辑处理,例如调用其他方法或触发事件等。
// 例如:emit('click', 'some data') 如果需要在父组件中接收点击事件的参数
}
</script>
<style lang="scss" scoped>
</style>
父组件 App.vue:
javascript
<template>
<MyButton @click="show" class="large" />
</template>
<script setup>
import MyButton from '@/components/Mybutton.vue';
function show() {
console.log('clicked');
}
</script>
<style lang="scss" scoped>
.large{
padding: 5px 10px;
}
</style>



可以看到,当子组件只有一个根节点,并没有向外传递任何 prop 或者 v-on 事件监听器时 ,却仍能够接收来自父组件的 class(子组件也有的话,就会进行合并) 和 事件(子组件也有的话,都会发生,但是先执行子组件的事件)。
总结:
(1)外部添加的 class 等 attribute 会和子组件根节点进行合并;
(2)外部定义的事件和子组件根节点定义的事件都会发生,但是内部更先执行。
1.2 深层组件继承(一代单传,代代相传)
将 1.1 单个根文件继承 中的代码进行改造。
创建一个 BaseButton.vue,使用原来子组件的代码:
javascript
<template>
<button class="btn" @click="handleClick">Click Me</button>
</template>
<script setup>
function handleClick() {
console.log('Button clicked!')
// 这里可以添加更多的逻辑处理,例如调用其他方法或触发事件等。
// 例如:emit('click', 'some data') 如果需要在父组件中接收点击事件的参数
}
</script>
<style lang="scss" scoped>
</style>
MyButton.vue 在根节点渲染 BaseButton 组件:
javascript
<template>
<BaseButton />
</template>
<script setup>
import BaseButton from './BaseButton.vue';
</script>
<style lang="scss" scoped>
</style>
在 App.vue 中再使用 MyButton 组件:
javascript
<template>
<MyButton @click="show" class="large" />
</template>
<script setup>
import MyButton from '@/components/Mybutton.vue';
function show() {
console.log('clicked');
}
</script>
<style lang="scss" scoped>
.large{
padding: 5px 10px;
}
</style>


发现 <MyButton> 组件透传的 attribute 和 v-on 事件监听器 会继续传给 <BaseButton>
2. 禁用根节点 Attributes 继承
2.1 使用方式(defineOptions( inheritAttrs: false}))
如果你不想要一个组件自动地继承 attribute,可以直接在 <script setup> 中使用 defineOptions:
javascript
<script setup>
defineOptions({
inheritAttrs: false
})
// ...setup 逻辑
</script>
将 1.1 中的 子组件 MyButton.vue 进行改造:
javascript
<template>
<button class="btn" @click="handleClick">Click Me</button>
</template>
<script setup>
defineOptions({
inheritAttrs: false
})
function handleClick() {
console.log('Button clicked!')
}
</script>
<style lang="scss" scoped>
</style>
父组件 App.vue 不变:
javascript
<template>
<MyButton @click="show" class="large" />
</template>
<script setup>
import MyButton from '@/components/Mybutton.vue';
function show() {
console.log('clicked');
}
</script>
<style lang="scss" scoped>
.large{
padding: 5px 10px;
}
</style>


发现父组件的 attributes 和 v-on 事件监听器 都无法透传到子组件中了。
2.2 指定非根节点继承 Attributes(结合 v-bind="$attrs)
我们可以通过设定 inheritAttrs: false 和使用 v-bind="$attrs" 来实现非根节点的继承。
子组件 MyButton.vue:
javascript
<template>
<!-- 在组件外包一层 -->
<div class="btn-wrapper">
<button class="btn" v-bind="$attrs" @click="handleClick">Click Me</button>
</div>
</template>
<script setup>
defineOptions({
inheritAttrs: false
})
function handleClick() {
console.log('Button clicked!')
}
</script>
<style lang="scss" scoped>
.btn-wrapper{
display: inline-flex;
padding: 20px;
background-color: aqua;
}
</style>
根组件 App.vue:
javascript
<template>
<MyButton @click="show" class="large" />
</template>
<script setup>
import MyButton from '@/components/Mybutton.vue';
function show() {
console.log('clicked');
}
</script>
<style lang="scss" scoped>
.large{
padding: 5px 10px;
}
</style>



可以看到:
(1)父组件透传的 attributes 和 v-on 事件监听器都传递给了指定的非跟元素;
(2)值得注意的是,此时父组件的事件执行顺序优先于子组件。和之前有所不同。
3. 多根节点的 Attribute 继承(使用 v-bind="$attrs" 指定一个继承)
子组件 MyButton.vue:
javascript
<template>
<!-- 多个根节点的组件没有自动 attribute 透传行为 -->
<button class="btn">Click Me 1</button>
<button class="btn" v-bind="$attrs" @click="handleClick">Click Me 2</button>
<button class="btn">Click Me 3</button>
</template>
<script setup>
function handleClick() {
console.log('Button clicked!')
}
</script>
<style lang="scss" scoped>
.btn{
margin-right: 10px;
}
</style>
父组件 App.vue:
javascript
<template>
<MyButton @click="show" class="large" />
</template>
<script setup>
import MyButton from '@/components/Mybutton.vue';
function show() {
console.log('clicked');
}
</script>
<style lang="scss" scoped>
.large{
padding: 5px 10px;
}
</style>


4. 在 JavaScript 中访问透传 Attributes(const attrs = useAttrs())
你可以在 <script setup> 中使用 useAttrs() API 来访问一个组件的所有透传 attribute。关键代码:
javascript
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
将 3 中的示例 子组件 MyButton.vue 进行改造,添加上述代码:
javascript
<template>
<!-- 多个根节点的组件没有自动 attribute 透传行为 -->
<button class="btn">Click Me 1</button>
<button class="btn" v-bind="$attrs" @click="handleClick">Click Me 2</button>
<button class="btn">Click Me 3</button>
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs()
console.log('attrs:', attrs)
function handleClick() {
console.log('Button clicked!')
}
</script>
<style lang="scss" scoped>
.btn{
margin-right: 10px;
}
</style>

上一章 《Vue3 组件 v-model》
下一章 《Vue3 插槽》