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