模版引用
ref 属性
场景:需要直接访问底层 DOM 元素。
方法:使用特殊的 ref 属性。
<input ref="input">
ref 属性 允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。
访问模板引用
小 Demo: 当 input 组件挂载后 自动获取焦点
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null)
onMounted(() => {
input.value.focus()
})
watchEffect(() => {
if (input.value) {
input.value.focus()
} else {
// 此时还未挂载,或此元素已经被卸载(例如通过 v-if 控制)
}
})
</script>
<template>
<div class="container">
<input ref="input" />
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
注意:只有组件挂载后才能访问模版引用。初次渲染时元素不存在所以在模版表达式上访问变量则为 null。
如果你需要侦听一个模板引用 ref 的变化,确保考虑到其值为 null 的情况
v-for 中的模板引用
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
const list = ref([
/* ... */
1, 2, 3
])
const itemRefs = ref([])
onMounted(() => console.log(itemRefs.value))
</script>
<template>
<div class="container">
<ul>
<li v-for="item in list" ref="itemRefs">
{{ item }}
</li>
</ul>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
效果:
应该注意的是,ref 数组并不保证与源数组相同的顺序。
函数模板引用
-
除了使用字符串作为名字,还可以使用一个函数,函数会在每次组件更新时都被调用。
-
该函数会收到元素引用作为其第一个参数
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
const input = ref([])
const sum = (el) => {
console.log(el)
}
</script>
<template>
<div class="container">
<button
:ref="
(el) => {
console.log(el)
}
"
>
绑定内联函数
</button>
<br />
<button :ref="sum">绑定组件函数</button>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
效果:
-
语法是 :ref 绑定函数。
-
可以绑定内联函数。
-
可以绑定组件函数。
-
当元素被销毁时,函数也会被调用一次,此时引用参数为 null。
组件上的 ref
模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import ComA from '@/components/ComA.vue'
const coma = ref<InstanceType<typeof ComA>>()
onMounted(() => {
console.log(coma.value)
})
</script>
<template>
<div class="container">
<ComA ref="coma"></ComA>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
效果:
一个子组件使用的是选项式 API ,父组件对子组件的每一个属性和方法都有完全的访问权。
使用了 <script setup> 的组件是默认私有的,一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露
defineExpose 宏
<script lang="ts" setup>
import { ref } from 'vue'
const a = ref(1)
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
</script>
<template>
<div class="container">我是子组件</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
未定义效果:
定义效果: