模板Refs
虽然Vue的声明式渲染模型为你抽象了大部分直接DOM操作,但在某些情况下,我们可能仍然需要直接访问底层DOM元素。为了实现这一点,我们可以使用特殊的ref
属性
模板
<input ref="input">
ref
是一个特殊属性,类似于在v-for
章节中讨论的key
属性。它允许我们在组件挂载后获得对特定DOM元素或子组件实例的引用。这可能在你想要,例如,在组件挂载时程序化地聚焦输入,或在一个元素上初始化第三方库时非常有用。
访问Refs
要使用组合式API获取引用,我们可以使用useTemplateRef()
辅助函数
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
// the first argument must match the ref value in the template
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="my-input" />
</template>
当使用TypeScript时,Vue的IDE支持和vue-tsc
将根据匹配的ref
属性所使用的元素或组件自动推断input.value
的类型。
3.5之前的用法
在3.5版本之前,由于还没有引入useTemplateRef()
,我们需要声明一个与模板ref属性值匹配的ref
vue
<script setup>
import { ref, onMounted } from 'vue'
// declare a ref to hold the element reference
// the name must match template ref value
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
如果不使用<script setup>
,请确保也从setup()
返回ref
js
export default {
setup() {
const input = ref(null)
// ...
return {
input
}
}
}
请注意,您只能在组件挂载后访问ref 。如果在模板表达式中尝试访问input
,则在第一次渲染时它将是null
。这是因为元素在第一次渲染后才会存在!
如果您正在尝试监视模板ref的变化,请确保考虑到ref值为null
的情况
js
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// not mounted yet, or the element was unmounted (e.g. by v-if)
}
})
另请参阅:[模板ref类型](/guide/typescript/composition-api#typing-template-refs)
在v-for
中的ref
需要v3.5或更高版本
当在v-for
中使用ref
时,相应的ref应该包含一个数组值,该数组值将在挂载后被填充
vue
<script setup>
import { ref, useTemplateRef, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = useTemplateRef('items')
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="items">
{{ item }}
</li>
</ul>
</template>
3.5之前的用法
在3.5版本之前,由于还没有引入useTemplateRef()
,我们需要声明一个与模板ref属性值匹配的ref。该ref也应包含一个数组值
vue
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</template>
需要注意的是,ref数组不保证与源数组相同的顺序。
函数ref
除了字符串键之外,ref
属性还可以绑定到一个函数,该函数将在每个组件更新时被调用,并为您提供完全的灵活性来存储元素引用。该函数接收元素引用作为第一个参数
模板
<input :ref="(el) => { /* assign el to a property or ref */ }">
请注意,我们正在使用动态的:ref
绑定,因此我们可以传递一个函数而不是ref名称字符串。当元素被卸载时,参数将是null
。当然,您也可以使用方法而不是内联函数。
组件上的ref
本节假设您了解组件。您可以随时跳过它,稍后再回来。
ref
也可以用于子组件。在这种情况下,引用将是组件实例的引用
vue
<script setup>
import { useTemplateRef, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = useTemplateRef('child')
onMounted(() => {
// childRef.value will hold an instance of <Child />
})
</script>
<template>
<Child ref="child" />
</template>
3.5之前的用法
vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value will hold an instance of <Child />
})
</script>
<template>
<Child ref="child" />
</template>
如果子组件使用Options API或没有使用<script setup>
,则
这里有一个例外,使用 <script setup>
的组件默认是 私有的:如果一个父组件通过 <script setup>
引用子组件,除非子组件选择使用 defineExpose
宏公开一个接口,否则父组件无法访问任何内容。
vue
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
// Compiler macros, such as defineExpose, don't need to be imported
defineExpose({
a,
b
})
</script>
当父组件通过模板引用获取此组件的实例时,获取到的实例将是 { a: number, b: number }
的形状(引用自动解包,就像在正常实例上一样)。
另请参阅:[组件模板引用的类型](/guide/typescript/composition-api#typing-component-template-refs)