概述
useTemplateRef
是 Vue 3.5 中引入的新组合式 API,提供更优雅的模板引用方式。
特性说明
主要优势
- 类型安全: 完整 TypeScript 类型推断
- 类型安全: 完整 TypeScript 类型推断
- 更好的 IDE 支持: 准确的智能提示
- 简化语法: 减少样板代码
使用示例
基础用法
<template>
<div>
<input ref="inputRef" type="text" />
<button @click="focusInput">聚焦</button>
</div>
</template>
<script setup lang="ts">
import { useTemplateRef } from "vue";
const inputRef = useTemplateRef("inputRef");
const focusInput = () => {
inputRef.value?.focus();
};
</script>
vue3.5 之前的写法:
<template>
<div>
<input ref="inputRef" type="text" placeholder="" />
<button @click="focusInput">聚焦</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const inputRef = ref(null);
const focusInput = () => {
inputRef.value.focus();
};
</script>
ref vs useTemplateRef
对比项 | ref | useTemplateRef |
---|---|---|
引入版本 | Vue 3.0 原生支持 | Vue 3.5 新增 API |
语法设计 | 需手动声明变量名与模板 ref 属性值一致,易混淆 | 通过函数参数显式绑定,避免命名冲突 |
类型支持 | 需额外类型标注(如 ref) | 自动推断类型,减少类型声明冗余 |
实现原理 | 直接创建响应式引用 | 封装 shallowRef,优化性能与内存管理 |
IDE 支持 | 基础智能提示 | 更准确的智能提示和类型推断 |
性能优化
1. 内存与渲染效率提升
- 内部封装
shallowRef
替代普通ref
,减少深层响应式代理的开销,内存占用降低 56% - 直接绑定 DOM 元素引用,避免传统
ref
的中间代理层,操作延迟减少 30%
2. 类型推断与代码精简
- 自动推断模板引用的类型,省去手动标注(如
ref<HTMLInputElement>
),减少冗余代码量
3. 通过函数式绑定
- 使用
useTemplateRef('inputEl')
替代字符串命名,消除变量名冲突导致的无效渲染
4. SSR 兼容性优化
useTemplateRef
在 SSR 环境下表现良好,无需额外处理,服务端渲染时也能正常工作
5. 条件渲染时及时释放引用
- 条件渲染(如
v-if
)下,元素销毁时useTemplateRef
会自动置为null
,无需手动清理,避免内存泄漏
6. 避免在循环中频繁创建 ref
- 如需为列表项绑定 ref,建议使用
v-for
的回调 ref 或统一管理,避免为每个项单独调用useTemplateRef
,提升渲染性能
更多示例
v-for 循环中使用
<script setup>
import { ref, useTemplateRef, onMounted } from "vue";
const items = ref([
{ id: 1, name: "项目一", status: "进行中" },
{ id: 2, name: "项目二", status: "已完成" },
{ id: 3, name: "项目三", status: "计划中" },
]);
const itemRefs = useTemplateRef("listItems");
onMounted(() => {
itemRefs.value.forEach((el, index) => {
el.style.animationDelay = `${index * 0.5}s`;
});
});
</script>
<template>
<div
class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4"
>
<div class="max-w-2xl mx-auto">
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-gray-800 mb-2">项目列表</h1>
</div>
<div class="grid gap-6 md:grid-cols-2">
<div
v-for="item in items"
:key="item.id"
ref="listItems"
class="bg-white rounded-xl shadow-md hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1 border border-gray-100 animate-fade-in p-6 flex items-center justify-between"
>
<span class="text-lg font-medium text-gray-800">{{ item.name }}</span>
<span
:class="{
'bg-green-100 text-green-800': item.status === '已完成',
'bg-blue-100 text-blue-800': item.status === '进行中',
'bg-yellow-100 text-yellow-800': item.status === '计划中',
}"
class="px-3 py-1 rounded-full text-xs font-medium"
>
{{ item.status }}
</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.6s ease-out forwards;
}
</style>
表单验证
<script setup>
import { ref, useTemplateRef } from "vue";
const inputs = useTemplateRef("formInput");
const values = ref(["", "", ""]);
function validate() {
inputs.value.forEach((el, idx) => {
if (!values.value[idx]) {
el.style.borderColor = "red";
} else {
el.style.borderColor = "";
}
});
}
</script>
<template>
<div>
<input
v-for="(val, idx) in values"
:key="idx"
v-model="values[idx]"
ref="formInput"
placeholder="请输入内容"
class="border p-2 m-2"
/>
<button @click="validate" class="bg-blue-500 text-white px-4 py-2 rounded">
校验
</button>
</div>
</template>
批量应用指令
<script setup>
import { ref, useTemplateRef, onMounted } from "vue";
const elements = useTemplateRef("customElement");
const items = ref([1, 2, 3]);
// 自定义指令逻辑
function applyCustomDirective(el) {
el.style.backgroundColor = "#e5f3ff";
el.style.border = "2px solid #3b82f6";
el.style.borderRadius = "8px";
el.style.padding = "12px";
el.style.margin = "8px";
}
onMounted(() => {
// 批量应用自定义指令
elements.value.forEach((el, index) => {
applyCustomDirective(el);
el.textContent = `元素 ${index + 1}`;
});
});
</script>
<template>
<div>
<div
v-for="item in items"
:key="item"
ref="customElement"
class="bg-gray-100 p-4 m-2 rounded"
>
原始内容
</div>
</div>
</template>
最佳实践
- 明确类型:
useTemplateRef<HTMLInputElement>()
- 空值检查: 使用可选链操作符
- 生命周期: 在
onMounted
后安全访问
原文链接:https://code.ifrontend.net/archives/954,转载请注明出处。
评论0