Vue 3 内置属性完全指南
本文档从零开始讲解 Vue 3 的内置属性:组件实例上由 Vue 提供的 $data、$props、$el、$refs、$parent、$root、$attrs、$slots、$emit、$nextTick 等,以及模板里的特殊属性 key、ref、is,配有大量示例,适合新手系统学习。
一、什么是“内置属性”?
1.1 两类“内置”东西
在 Vue 里,“内置”大致分两类:
- 组件实例上的内置属性:每个组件实例上都有一些以 $ 开头的属性或方法,如 $data、$refs、$nextTick,由 Vue 自动挂上去,用来访问数据、DOM、父/根实例等。
- 模板里的特殊属性:如 key、ref、is,在模板里写时有特殊含义,不是普通 HTML 属性。
本文先讲实例上的内置属性,再讲模板里的特殊属性。
在 里没有 this,部分内置能力通过 defineExpose、useAttrs、useSlots、getCurrentInstance 等使用,文中会一并说明。
二、$data:组件的数据对象
2.1 是什么?
$data 指向组件 data 选项返回的响应式对象。
在选项式 API 里,this.$data 和直接写 this.xxx(data 里的属性)是同一份数据。
2.2 使用场景
一般直接用 this.xxx 即可,很少直接写 this.$data。
需要“遍历所有 data 字段”或“把整个 data 序列化”时可以用 this.$data。
export default {
data() {
return { count: 0, name: '' }
},
mounted() {
console.log(this.$data)
console.log(this.$data === this) // false,$data 是 data 返回的对象
console.log(this.$data.count === this.count)
}
}
2.3 在 里
没有 this,没有 $data;数据就是你定义的 ref、reactive,直接访问即可。
三、$props:当前组件接收的 props
3.1 是什么?
$props 是父组件传入的 props 组成的对象,和 defineProps 或 props 选项对应,是只读、响应式的。
3.2 使用场景
在选项式 API 里需要“把整个 props 传给子组件”或“遍历 props”时可用 this.$props。
export default {
props: ['title', 'count'],
mounted() {
console.log(this.$props)
}
}
在 里用 defineProps() 的返回值,没有 $props 这个名字,但含义相同。
四、$el:根 DOM 元素
4.1 是什么?
$el 是组件根 DOM 元素。
只有组件挂载完成后才有(在 mounted 及之后可用);若组件有多个根节点(Vue 3 Fragment),$el 指向第一个根节点。
在 里没有 this,要用 $el 需通过 ref 绑定根元素,或 getCurrentInstance()?.proxy?.$el(不推荐依赖,建议用 ref)。
4.2 使用场景
需要拿到“整个组件根节点”时(如交给第三方库、测量尺寸),在选项式里可用 this.$el。
export default {
mounted() {
console.log(this.$el)
this.$el.style.border = '1px solid red'
}
}
五、$refs:模板 ref 的集合
5.1 是什么?
在模板里写的 ref=”xxx”,会在 this.$refs.xxx 上暴露出来:
- 绑在 DOM 上,$refs.xxx 是该 DOM 元素;
- 绑在子组件上,$refs.xxx 是该子组件的实例(或子组件通过 defineExpose 暴露的内容)。
5.2 使用场景
需要直接操作子 DOM 或调用子组件方法时用 $refs。
$refs 在 mounted 之后才有,且 v-if 控制的 ref 可能在未渲染时为 undefined。
<template>
<input ref="inputRef" />
<Child ref="childRef" />
</template>
<script>
export default {
mounted() {
this.$refs.inputRef?.focus()
this.$refs.childRef?.someMethod()
}
}
</script>
在 里不用 $refs,而是自己 const inputRef = ref(null),模板里 ref=”inputRef”,用 inputRef.value 访问。
六、$parent 与 $root
6.1 $parent
$parent 是父组件实例(若存在)。
通过 this.$parent 可以访问父组件的 data、methods 等,但不推荐用这种方式通信,优先用 props + emit 或 provide/inject。
mounted() {
console.log(this.$parent)
}
6.2 $root
$root 是当前组件树的根实例(即 createApp(App) 挂载的那个根)。
可用于“跨多层”访问根上的数据或方法,但更推荐 provide/inject 或 Pinia。
mounted() {
console.log(this.$root)
}
在 里没有 this,可用 getCurrentInstance()?.parent、getCurrentInstance()?.root,同样不推荐依赖,仅作了解。
七、$attrs:未声明为 props 的属性
7.1 是什么?
父组件在子组件上绑了没有在子组件 props(或 emits)里声明的属性/事件,会落在 $attrs 里。
例如父写了 :id=”xxx”、class=”box”,子没有声明 id,那 $attrs 里就有 id、class 等。
常用于属性透传:在子组件根节点上写 v-bind=”$attrs”,把这些属性继续往下传。
7.2 使用场景
<!-- 父 -->
<MyInput id="my-id" class="input" placeholder="请输入" />
<!-- 子:不声明 id、class,它们会进 $attrs -->
<template>
<input v-bind="$attrs" />
</template>
<script>
export default {
inheritAttrs: false
}
</script>
在 里用 useAttrs() 拿到和 $attrs 等价的对象。
import { useAttrs } from 'vue'
const attrs = useAttrs()
八、$slots:插槽内容
8.1 是什么?
$slots 是一个对象,键是插槽名(default 表示默认插槽),值是对应的插槽内容(VNode 或函数)。
用来判断“有没有传入某插槽”或“在 JS 里渲染插槽”。
8.2 使用场景
export default {
mounted() {
console.log(this.$slots.default)
console.log(this.$slots.header)
}
}
模板里一般用 、 即可;需要“根据是否有插槽再决定是否渲染某块”时,可判断 this.$slots.xxx。
在 里用 useSlots()。
import { useSlots } from 'vue'
const slots = useSlots()
if (slots.header) { ... }
九、$emit:触发自定义事件
9.1 是什么?
$emit 是组件用来向父组件触发自定义事件的方法:this.$emit(‘事件名’, 参数)。
父组件用 @事件名 监听。
9.2 使用场景
export default {
methods: {
submit() {
this.$emit('submit', { id: 1 })
}
}
}
在 里用 defineEmits() 返回的 emit 函数,不用 this.$emit。
十、$nextTick:DOM 更新后执行
10.1 是什么?
$nextTick 接收一个回调(或返回 Promise),在 Vue 把数据变化应用到 DOM 之后再执行。
用于“改完数据后立刻操作 DOM”的场景,如滚动到底部、focus 到新出现的输入框。
10.2 使用场景
this.list.push(newItem)
this.$nextTick(() => {
this.$refs.list.scrollTop = this.$refs.list.scrollHeight
})
在 里从 vue 引入 nextTick,直接 await nextTick() 或 nextTick(fn),不用 this.$nextTick。
import { nextTick } from 'vue'
await nextTick()
十一、$options:组件配置
11.1 是什么?
$options 是组件在定义时传入的配置对象(即 export default { … } 或 defineComponent 的那一坨),包含 name、data、methods 等。
也可在这里挂自定义属性,如 this.$options.myOption。
11.2 使用场景
export default {
name: 'MyComp',
myOption: 'hello',
mounted() {
console.log(this.$options.name)
console.log(this.$options.myOption)
}
}
一般用于取 name、自定义选项或高阶场景,新手较少直接用。
十二、getCurrentInstance(在 setup 里拿“实例”)
12.1 是什么?
在 或 setup() 里没有 this,若需要访问当前组件实例(例如拿 $el、$parent),可以用 getCurrentInstance()。
返回的 instance 上有 proxy(约等于 this)、parent、root 等,多用于库或高级用法。
12.2 使用场景
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
console.log(instance?.proxy?.$el)
console.log(instance?.parent)
注意:getCurrentInstance 只在 setup 或 同步生命周期 里有效,且官方不推荐在业务里强依赖内部实例,优先用 ref、provide/inject、props/emit。
十三、模板里的特殊属性:key
13.1 是什么?
key 是 Vue 在列表渲染或条件切换时用来唯一标识节点的特殊属性。
在 v-for 里必须给每项一个稳定、唯一的 key,便于 Vue 正确复用或更新 DOM;在 或组件上写 key 可强制“不同 key 当不同实例”重新创建。
13.2 使用场景
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
<component :is="current" :key="current" />
key 改变时,Vue 会先销毁旧节点再创建新节点,可用于“切换 tab 时重置组件状态”。
十四、模板里的特殊属性:ref
14.1 是什么?
ref 在模板里用于把 DOM 或子组件实例绑定到 script 里的一个 ref 变量。
在选项式里绑定到 this.$refs.xxx;在 里绑定到你声明的 ref(null)。
14.2 使用场景
<input ref="inputRef" />
const inputRef = ref(null)
onMounted(() => {
inputRef.value?.focus()
})
详见《Vue3组件.md》中“模板 ref”一节。
十五、模板里的特殊属性:is
15.1 是什么?
is 用于动态组件::is=”组件名或组件对象” 决定当前渲染哪个组件。
常与 配合。
15.2 使用场景
<component :is="currentTabComponent" />
const currentTabComponent = ref('TabHome')
或 :is 用在原生 HTML 标签上,用于解析为 Vue 组件(如 把本文档里的 $refs、$emit、$nextTick、key、ref 在项目里用一遍,会掌握得更牢。祝你学习顺利。 里用
),新手较少用到。
十六、内置属性 / 特殊属性速查表
名称
类型
含义
在 中
$data
实例属性
data 返回的对象
无 this,直接用 ref/reactive
$props
实例属性
当前 props 对象
defineProps() 的返回值
$el
实例属性
根 DOM 元素
用 ref 绑根元素或 getCurrentInstance
$refs
实例属性
模板 ref 集合
自己 ref(null) + 模板 ref=”变量”
$parent
实例属性
父组件实例
getCurrentInstance()?.parent
$root
实例属性
根实例
getCurrentInstance()?.root
$attrs
实例属性
未声明的属性/事件
useAttrs()
$slots
实例属性
插槽内容
useSlots()
$emit
实例方法
触发自定义事件
defineEmits() 返回的 emit
$nextTick
实例方法
DOM 更新后执行回调
import { nextTick } from ‘vue’
$options
实例属性
组件配置对象
一般不直接用
key
模板属性
节点唯一标识
同左
ref
模板属性
绑定 DOM/组件到 ref
同左
is
模板属性
动态组件
同左
十七、学习建议