$refs 的使用:Vue3 Composition API
问题描述
在 Vue 3 的 Composition API 中,开发者经常遇到无法正确获取 $refs 的问题。当需要在组件挂载后访问特定的 DOM 元素或子组件实例时,传统的 Options API 方式不再适用。
Options API 中的写法:
javascript
mounted() {
console.log("Mounted - ok");
console.log(this.$refs.table.temp());
}但在 Composition API 中直接使用 getCurrentInstance().$refs 会导致错误:
javascript
setup() {
const that: any = getCurrentInstance();
onMounted(() => {
console.log("Mounted - ok");
console.log(that.$refs.table.temp()); // ERROR: that.$refs is undefined
});
return {};
}解决方案
基础用法:使用 ref()
在 Composition API 中,应该使用 ref() 函数创建引用,并在模板中使用相同的名称:
vue
<template>
<div ref="table"/>
</template>
<script>
import { ref, onMounted } from 'vue';
setup() {
const table = ref(null);
onMounted(() => {
console.log(table.value); // 现在可以正常访问
});
return { table };
}
</script>重要提示
ref 的值在组件挂载完成后才会被填充。在首次渲染期间,table.value 将为 null,因为此时元素还不存在。
访问子组件方法
如果需要访问子组件的方法,需要确保子组件通过 setup() 的返回值暴露这些方法:
子组件代码:
vue
<template>
<h1>子组件</h1>
</template>
<script>
export default {
setup(props, ctx) {
const tempMethod = () => {
console.log("子组件方法");
}
return {
tempMethod // 暴露方法
}
},
}
</script>父组件代码:
vue
<template>
<MyCompo ref="table"/>
</template>
<script>
import MyCompo from "@/components/MyCompo.vue"
import { ref, onMounted } from 'vue'
export default {
components: {
MyCompo
},
setup(props, ctx) {
const table = ref(null);
onMounted(() => {
table.value.tempMethod() // 正确调用子组件方法
});
return { table };
}
}
</script>处理条件渲染的引用
当引用元素位于条件渲染块中时,需要特别注意:
vue
<template>
<div v-if="isVisible">
<div ref="table"/>
</div>
</template>
<script>
import { ref, onMounted, watch } from 'vue';
setup() {
const table = ref(null);
const isVisible = ref(false);
// 监听可见性变化
watch(isVisible, (newVal) => {
if (newVal) {
// 此时 table.value 可用
console.log(table.value);
}
});
return { table, isVisible };
}
</script>处理列表中的多个引用
对于 v-for 循环中的多个元素引用,可以使用函数式引用:
vue
<template>
<div v-for="(item, i) in items"
:ref="el => elements[i] = el"
:key="item.id">
<div>ID: {{ item.id }}</div>
<div>Name: {{ item.name }}</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
setup() {
const items = [
{id: 1, name: "项目1"},
{id: 2, name: "项目2"},
{id: 3, name: "项目3"},
];
const elements = ref([]);
onMounted(() => {
console.log(elements.value.map(el => el.textContent));
});
return { elements, items };
}
</script>注意
在 Vue 3.2.25 到 3.2.31 版本中,使用 ref="elements" 方式在 v-for 中可能会得到空数组,使用函数式引用可以避免这个问题。
TypeScript 支持
使用 TypeScript 时,可以为引用添加类型注解:
typescript
const table = ref<HTMLDivElement | null>(null);Vue 3.5+ 推荐方式:useTemplateRef
从 Vue 3.5 开始,官方推荐使用 useTemplateRef 来获取模板引用(需检查具体版本支持情况)。
总结
在 Vue 3 Composition API 中使用 $refs 的关键点:
- 使用
ref()创建引用变量 - 在模板中使用相同名称的
ref属性 - 引用值在组件挂载后才可用
- 条件渲染的元素需要额外处理
- 列表引用推荐使用函数式引用方式
- 子组件需要显式暴露方法才能被访问
遵循这些模式,可以避免常见的引用访问问题,并在 Composition API 中正确使用模板引用功能。