vue 3 中 attrs 和 listeners 统一为 $attrs,事件如 onclick 作为键名存在;推荐 useattrs() 获取响应式 attrs;单根组件默认透传,可设 inheritattrs: false 禁用;v-bind="$attrs" 自动透传事件与属性。

在 Vue 3 的 <script setup></script> 中,attrs 和 listeners 不再分离——它们统一收归为 $attrs 对象,事件监听器(如 @click、@update:modelValue)也作为属性键名(如 onClick、onUpdate:modelValue)进入其中。Vue 不再提供独立的 listeners 对象,这是与 Vue 2 的关键区别。
显式获取 attrs:用 useAttrs()
推荐使用 useAttrs() 显式获取响应式 attrs 对象:
- 它返回一个响应式只读代理,可安全用于
computed或模板绑定 - 支持 TypeScript 类型推导,VuReact 等工具也依赖此 API 进行静态分析
- 避免使用已废弃的
context.attrs或隐式$attrs(非组合式 API 中的写法)
示例:
const attrs = useAttrs()
// 可直接访问 attrs.class、attrs.onClick、attrs['data-id']
隐式透传:默认行为与 inheritAttrs 控制
单根组件默认自动将未声明的属性和事件挂载到根元素,无需代码干预。但该行为可被关闭:
- 添加
defineOptions({ inheritAttrs: false })即禁用自动透传 - 禁用后,
attrs仍存在且可用,只是不再“自动落到根上” - 适用于根是容器(如
<div>),而真实承载属性的是内部 <code><input>或第三方组件的场景透传 listeners:v-bind="$attrs" 天然支持
事件监听器不是特殊存在,而是
attrs的一部分。只要执行v-bind="$attrs",所有事件(包括v-model拆解出的onUpdate:modelValue)都会原样绑定:- 父组件写
<myinput v-model="v"></myinput> - 子组件内
<input v-bind="$attrs">→ 自动获得onChange、onFocus、modelValue和onUpdate:modelValue - 无需手动解构或重 emit,双向绑定与事件转发一步到位
选择性透传与优先级控制
直接
v-bind="$attrs"简单,但可能覆盖组件内部逻辑设定的值(如固定type="text"被父级type="number"覆盖)。此时应主动控制:- 用
computed合并:size: computed(() => attrs.size ?? 'medium') - 对特定属性单独绑定:
:placeholder="attrs.placeholder",其余不透传 - 注意短横线命名属性必须用方括号访问:
attrs['data-test-id'] - 已通过
emits声明的事件(如emits: ['click'])不会出现在attrs中,不会重复触发
- 父组件写










