模板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)