props是显式声明的组件输入,用于父子通信;attrs是未被props声明的剩余属性,默认自动绑定到根元素。两者核心区别在于:props显式、类型安全、单向;attrs隐式、动态、默认透传。

Props 是显式声明的组件输入,用于父子通信;Attrs 是未被 props 声明的剩余属性,默认会自动绑定到根元素上——这是两者最核心的区别。而“非属性特性继承控制”,本质是通过 v-bind="$attrs" 和 inheritAttrs: false 主动干预 attrs 的默认透传行为。
Props:显式、类型安全、单向数据流
Props 是组件 API 的契约。父组件通过属性形式传值,子组件必须在 props 选项中显式声明(Options API)或使用 defineProps(Composition API),否则 Vue 会警告。
- 支持类型校验(
type、required、default、validator) - 仅用于父子通信,不参与 DOM 渲染(除非手动绑定到某个元素)
- 修改 props 会触发警告(应通过
emit通知父组件更新) - 示例:
<myinput label="用户名" size="large"></myinput>中,若子组件只声明了label,则size不会进入 props,而是落入 $attrs
Attrs:隐式、动态、默认挂载到根元素
$attrs 是一个对象,包含所有未被 props 捕获的绑定属性(如 class、style、id、自定义属性等),还包括 v-on 绑定的事件监听器(Vue 3 起事件也归入 attrs)。
- 默认行为:Vue 自动将 $attrs “透传”到组件模板的根元素上(如根是
<input>,则id="xxx"会直接出现在该 input 上) - 当组件有多个根节点(fragments)时,$attrs 不再自动挂载,需手动用
v-bind="$attrs"分发 - 常见用途:封装基础组件(如
BaseButton)时,让使用者传入原生 button 支持的任意属性
关闭自动继承:inheritAttrs: false
设为 false 后,$attrs 不再自动绑定到根元素,但 $attrs 对象本身仍存在,可由开发者完全自主控制如何使用。
- 典型场景:自定义组件需要把某些 attrs 绑定到内部非根元素(如把
placeholder给内部<input>,而非根<div>)<li>常配合 <code>v-bind="$attrs"使用,实现精准透传 - 注意:设为 false 后,class 和 style 也不再自动继承——如需保留,要显式写
:class="$attrs.class"或合并处理
组合实践:封装一个带 label 的输入框
目标:父组件传 label(props)、placeholder 和 disabled(attrs),且不污染根 div。
// 子组件 MyInput.vue
<template><div class="my-input">
<label>{{ label }}</label>
<input v-bind="$attrs"><!-- placeholder/disabled 等透传给 input -->
</div>
</template><p><script setup>
const props = defineProps({
label: String
})
// inheritAttrs 默认 true,但我们希望 attrs 不挂在 div 上 → 关闭
defineOptions({ inheritAttrs: false })
</script></p>
这样既保持了 props 的清晰接口,又让 attrs 灵活服务于内部真实 DOM 节点。











