跳转到内容

穿透属性

本页假设您已经阅读了组件基础。如果是组件新手,请先阅读该内容。

属性继承

“穿透属性”是指传递给组件但未在接收组件的propsemits中显式声明的属性或v-on事件监听器。常见示例包括classstyleid属性。

当一个组件渲染单个根元素时,穿透属性将自动添加到根元素的属性中。例如,给定一个具有以下模板的<MyButton>组件

template
<!-- template of <MyButton> -->
<button>Click Me</button>

并且一个父组件使用此组件

template
<MyButton class="large" />

最终的渲染DOM将是

html
<button class="large">Click Me</button>

在这里,<MyButton>未声明class为接受的prop。因此,class被视为穿透属性并自动添加到<MyButton>的根元素。

classstyle合并

如果子组件的根元素已经存在 classstyle 属性,它们将与从父组件继承的 classstyle 值合并。假设我们将上一个示例中的 <MyButton> 模板进行修改

template
<!-- template of <MyButton> -->
<button class="btn">Click Me</button>

那么最终渲染的 DOM 将变为

html
<button class="btn large">Click Me</button>

v-on 监听器继承

相同的规则也适用于 v-on 事件监听器

template
<MyButton @click="onClick" />

click 监听器添加到 <MyButton> 的根元素上,即原生的 <button> 元素。当原生 <button> 被点击时,它将触发父组件的 onClick 方法。如果原生 <button> 已经通过 v-on 绑定了 click 监听器,则两个监听器都将被触发。

嵌套组件继承

如果一个组件将其根节点渲染为另一个组件,例如,我们将 <MyButton> 重构为渲染 <BaseButton> 作为其根节点

template
<!-- template of <MyButton/> that simply renders another component -->
<BaseButton />

那么 <MyButton> 接收到的透传属性将自动转发给 <BaseButton>

注意

  1. 透传属性不包括任何被声明为 props 或由 <MyButton> 声明的事件的 v-on 监听器——换句话说,声明的 props 和监听器已经被 <MyButton> “消费”了。

  2. 如果 <BaseButton> 声明,则可以将其作为 props 接受透传属性。

禁用属性继承

如果您不希望组件自动继承属性,可以在组件的选项中设置 inheritAttrs: false

从 3.3 版本开始,您也可以在 <script setup> 中直接使用 defineOptions

vue
<script setup>
defineOptions({
  inheritAttrs: false
})
// ...setup logic
</script>

禁用属性继承的常见场景是当需要将属性应用于根节点以外的其他元素时。通过将 inheritAttrs 选项设置为 false,您可以完全控制透传属性应该应用的位置。

这些透传属性可以直接在模板表达式中作为 $attrs 访问

template
<span>Fallthrough attributes: {{ $attrs }}</span>

$attrs 对象包括所有未由组件的 propsemits 选项声明的属性(例如,classstylev-on 监听器等)。

一些注意事项

  • 与 props 不同,透传属性在 JavaScript 中保留了它们的原始大小写,因此像 foo-bar 这样的属性需要按 $attrs['foo-bar'] 访问。

  • @click 这样的 v-on 事件监听器将在对象上作为 $attrs.onClick 下的函数暴露。

使用我们之前的 <MyButton> 组件示例——有时我们可能需要为了样式目的将实际的 <button> 元素包裹在一个额外的 <div>

template
<div class="btn-wrapper">
  <button class="btn">Click Me</button>
</div>

我们希望所有像 classv-on 监听器这样的透传属性都应用于内层的 <button>,而不是外层的 <div>。我们可以通过设置 inheritAttrs: falsev-bind="$attrs" 来实现这一点

template
<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">Click Me</button>
</div>

请记住,v-bind 没有参数 将对象的所有属性绑定为目标元素的属性。

多根节点上的属性继承

与单根节点的组件不同,多根节点的组件没有自动属性穿透行为。如果未显式绑定 $attrs,则会发出运行时警告。

template
<CustomLayout id="custom-layout" @click="changeValue" />

如果 <CustomLayout> 有以下多根模板,将会有警告,因为 Vue 无法确定应用属性穿透的位置

template
<header>...</header>
<main>...</main>
<footer>...</footer>

如果显式绑定 $attrs,则警告将被抑制

template
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

在 JavaScript 中访问属性穿透

如果需要,您可以在 <script setup> 中使用 useAttrs() API 访问组件的属性穿透

vue
<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

如果不使用 <script setup>,则 attrs 将作为 setup() 上下文的一个属性公开

js
export default {
  setup(props, ctx) {
    // fallthrough attributes are exposed as ctx.attrs
    console.log(ctx.attrs)
  }
}

请注意,尽管这里的 attrs 对象总是反映最新的属性穿透,但它不是响应式的(出于性能原因)。您不能使用观察者来观察其变化。如果您需要响应式,请使用 prop。或者,您可以使用 onUpdated() 在每次更新时使用最新的 attrs 执行副作用。

如果需要,您可以通过 $attrs 实例属性访问组件的属性穿透

js
export default {
  created() {
    console.log(this.$attrs)
  }
}
属性穿透已加载