Vue 3.5 重磅新特性:useTemplateRef 讓模板引用更優雅、更高效!

Vue 3.5 重磅新特性:useTemplateRef 讓模板引用更優雅、更高效!

目錄

  • 前言
  • 什么是 useTemplateRef
  • 傳統 ref 的問題
  • useTemplateRef 的優勢
  • 基礎用法
  • 進階用法
  • 最佳實踐
  • 遷移指南
  • 性能對比
  • 注意事項
  • 總結

前言

Vue 3.5 帶來了一個激動人心的新特性 useTemplateRef,它徹底革新了我們在 Vue 3 中處理模板引用的方式。這個新的 Composition API 不僅讓代碼更加優雅,還提供了更好的類型安全性和性能優化。

什么是 useTemplateRef

useTemplateRef 是 Vue 3.5 新增的 Composition API,專門用于處理模板引用(template refs)。它提供了一種更直觀、更類型安全的方式來訪問 DOM 元素和組件實例。

核心特性

  • ?? 類型安全:完美的 TypeScript 支持
  • ?? 性能優化:更高效的內部實現
  • ?? 簡潔語法:更直觀的 API 設計
  • ?? 響應式:與 Vue 的響應式系統深度集成

傳統 ref 的問題

在 Vue 3.5 之前,我們通常這樣處理模板引用:

問題示例

<template><div><input ref="inputRef" /><button @click="focusInput">聚焦輸入框</button><MyComponent ref="componentRef" /></div>
</template><script setup lang="ts">
import { ref, onMounted } from 'vue'
import MyComponent from './MyComponent.vue'// 問題1:類型推斷不夠精確
const inputRef = ref<HTMLInputElement>()
const componentRef = ref<InstanceType<typeof MyComponent>>()// 問題2:需要手動類型斷言
const focusInput = () => {inputRef.value?.focus() // 需要可選鏈
}// 問題3:在 onMounted 之前 ref.value 為 undefined
onMounted(() => {console.log(inputRef.value) // 可能為 undefined
})
</script>

存在的問題

  1. 類型推斷復雜:需要手動指定泛型類型
  2. 運行時檢查:需要使用可選鏈操作符
  3. 生命周期依賴:只能在特定生命周期后使用
  4. 代碼冗余:重復的類型聲明和空值檢查

useTemplateRef 的優勢

1. 類型安全

<script setup lang="ts">
import { useTemplateRef } from 'vue'// 自動類型推斷,無需手動指定類型
const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
const buttonRef = useTemplateRef<HTMLButtonElement>('buttonRef')// TypeScript 會自動推斷出正確的類型
const focusInput = () => {inputRef.value?.focus() // 完美的類型提示
}
</script>

2. 更好的性能

// 內部優化,減少不必要的響應式開銷
const elementRef = useTemplateRef('elementRef')// 自動優化,只在需要時創建響應式引用

3. 簡潔的 API

<template><input ref="inputRef" /><button ref="buttonRef" @click="handleClick">點擊</button>
</template><script setup lang="ts">
import { useTemplateRef } from 'vue'// 一行代碼搞定
const inputRef = useTemplateRef<HTMLInputElement>('inputRef')
const buttonRef = useTemplateRef<HTMLButtonElement>('buttonRef')const handleClick = () => {inputRef.value?.focus()
}
</script>

基礎用法

1. DOM 元素引用

<template><div><input ref="usernameInput" placeholder="請輸入用戶名"@keyup.enter="handleSubmit"/><button ref="submitButton" @click="handleSubmit">提交</button><div ref="messageContainer"></div></div>
</template><script setup lang="ts">
import { useTemplateRef, nextTick } from 'vue'// 創建模板引用
const usernameInput = useTemplateRef<HTMLInputElement>('usernameInput')
const submitButton = useTemplateRef<HTMLButtonElement>('submitButton')
const messageContainer = useTemplateRef<HTMLDivElement>('messageContainer')// 聚焦輸入框
const focusUsername = () => {usernameInput.value?.focus()
}// 提交處理
const handleSubmit = async () => {const username = usernameInput.value?.valueif (!username) {await showMessage('請輸入用戶名', 'error')focusUsername()return}// 禁用按鈕if (submitButton.value) {submitButton.value.disabled = true}try {// 模擬 API 調用await submitForm(username)await showMessage('提交成功!', 'success')} catch (error) {await showMessage('提交失敗,請重試', 'error')} finally {// 恢復按鈕狀態if (submitButton.value) {submitButton.value.disabled = false}}
}// 顯示消息
const showMessage = async (text: string, type: 'success' | 'error') => {if (!messageContainer.value) returnmessageContainer.value.textContent = textmessageContainer.value.className = `message ${type}`await nextTick()// 3秒后清除消息setTimeout(() => {if (messageContainer.value) {messageContainer.value.textContent = ''messageContainer.value.className = ''}}, 3000)
}// 模擬 API 調用
const submitForm = (username: string): Promise<void> => {return new Promise((resolve, reject) => {setTimeout(() => {Math.random() > 0.3 ? resolve() : reject(new Error('網絡錯誤'))}, 1000)})
}
</script><style scoped>
.message {padding: 8px;margin-top: 10px;border-radius: 4px;
}.message.success {background-color: #d4edda;color: #155724;border: 1px solid #c3e6cb;
}.message.error {background-color: #f8d7da;color: #721c24;border: 1px solid #f5c6cb;
}
</style>

2. 組件實例引用

<template><div><UserProfile ref="userProfileRef" :user-id="currentUserId"/><AdminPanel ref="adminPanelRef" v-if="isAdmin"/><button @click="refreshUserData">刷新用戶數據</button><button @click="openAdminSettings" v-if="isAdmin">管理員設置</button></div>
</template><script setup lang="ts">
import { useTemplateRef, ref } from 'vue'
import UserProfile from './components/UserProfile.vue'
import AdminPanel from './components/AdminPanel.vue'// 組件引用
const userProfileRef = useTemplateRef<InstanceType<typeof UserProfile>>('userProfileRef')
const adminPanelRef = useTemplateRef<InstanceType<typeof AdminPanel>>('adminPanelRef')// 數據
const currentUserId = ref(123)
const isAdmin = ref(true)// 刷新用戶數據
const refreshUserData = async () => {// 調用子組件的方法await userProfileRef.value?.refreshData()// 獲取子組件的數據const userData = userProfileRef.value?.getUserData()console.log('用戶數據:', userData)
}// 打開管理員設置
const openAdminSettings = () => {// 調用管理員面板的方法adminPanelRef.value?.openSettings()// 訪問管理員面板的狀態const isSettingsOpen = adminPanelRef.value?.settingsVisibleconsole.log('設置面板狀態:', isSettingsOpen)
}
</script>

3. 動態引用

<template><div><div v-for="(item, index) in items" :key="item.id":ref="el => setItemRef(el, index)"class="item">{{ item.name }}</div><button @click="highlightRandomItem">隨機高亮</button></div>
</template><script setup lang="ts">
import { ref, onUpdated } from 'vue'interface Item {id: numbername: string
}const items = ref<Item[]>([{ id: 1, name: '項目 1' },{ id: 2, name: '項目 2' },{ id: 3, name: '項目 3' },{ id: 4, name: '項目 4' },{ id: 5, name: '項目 5' }
])// 動態引用集合
const itemRefs = ref<HTMLDivElement[]>([])// 設置動態引用
const setItemRef = (el: Element | null, index: number) => {if (el && el instanceof HTMLDivElement) {itemRefs.value[index] = el}
}// 清理無效引用
onUpdated(() => {itemRefs.value = itemRefs.value.slice(0, items.value.length)
})// 高亮隨機項目
const highlightRandomItem = () => {// 清除之前的高亮itemRefs.value.forEach(el => {if (el) {el.classList.remove('highlight')}})// 隨機選擇一個項目高亮const randomIndex = Math.floor(Math.random() * items.value.length)const targetElement = itemRefs.value[randomIndex]if (targetElement) {targetElement.classList.add('highlight')targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' })}
}
</script><

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/95447.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/95447.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/95447.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uni app 的app端 寫入運行日志到指定文件夾。

uni app 的app 端 寫入指定目錄文件夾。并自動生成當前日期的日志文件。刪除十日前的日志文件其中 writefile.js 代碼如下const {default: logger } require("./logger")var name var url var params var method var resfunction setlog(name, url, params, method)…

桌面應用開發語言與框架選擇指南

桌面應用開發的語言和框架選擇非常豐富&#xff0c;從原生性能到跨平臺解決方案應有盡有。下面我將它們分為幾大類進行詳細介紹&#xff0c;并附上各自的優缺點和適用場景。 一、 原生開發 (Native Development) 原生開發能提供最佳的性能和與操作系統最完美的集成體驗。 1. …

C++知識

文章目錄1.Cmap為什么線程不安全?2.map大量插入會有性能問題&#xff0c;為什么3.set的應用場景4.map set mutiset mutimap unordered_map unordered_set的底層實現、使用場景、優缺點1.Cmap為什么線程不安全? 其實STL中的容器都是線程不安全的&#xff0c;如果想要線程安全…

自學嵌入式第三十四天:網絡編程-TCP

一、UDP用戶數據報收發次數要對應&#xff1b;數據與數據之間有邊界&#xff0c;多次調用收發時都是不同的數據報&#xff1b;接收方的數據大小>發送方的數據大小&#xff0c;如果接受方數據小了則會丟棄未讀的部分&#xff0c;再次調用只會讀下一包數據&#xff1b;二、服務…

Apache IoTDB:國產時序數據庫的崛起與工業物聯網的未來

&#x1f4d1;前言 在工業物聯網的浪潮中&#xff0c;數據不再是副產品&#xff0c;而是驅動決策的核心資產。"隨著物聯網、工業互聯網和智能監控的迅猛發展&#xff0c;時序數據正以前所未有的速度爆發。據預測&#xff0c;到2025年全球物聯網設備將達750億臺&#xff0c…

一鍵核驗,安全無憂!手機號三要素詳情版API,為您的業務筑牢身份認證防線

一、什么是手機號三要素核驗API&#xff1f; 手機號三要素核驗API 是一種通過編程接口&#xff0c;實時驗證一條個人身份信息是否與該國運營商登記的實名信息一致的在線服務。 這里的“三要素”特指&#xff1a; 姓名 身份證號碼 手機號碼 核驗過程&#xff1a;用戶提交上述三個…

輕松上手 qData 數據中臺開源版:Docker Compose 助你10分鐘跑起來

說在前面 誰適合看這份指南&#xff1f; 初次接觸 qData&#xff0c;希望快速體驗功能的小伙伴不想折騰復雜環境配置和前端打包的人想用“一鍵啟動”省事體驗完整平臺的用戶 我們已經為你準備好“開箱即用”的完整部署包&#xff0c;包括&#xff1a; ? 前端靜態資源&…

Qt讀寫Excel--QXlsx基本使用

1、概述 Document 類是一個用于操作 XLSX 文件的類&#xff0c;繼承自 QObject。它提供了對 Excel 文件的讀寫操作&#xff0c;包括單元格的讀寫、圖片和圖表的插入、單元格合并、列和行的格式化、數據驗證和條件格式化等功能。此外&#xff0c;它還支持對工作簿和工作表的操作…

P13929 [藍橋杯 2022 省 Java B] 山 題解

縮減一下題目的意思&#xff0c;問區間 [2022,2022222022] 有多少個數是回文數并且先單調不減&#xff0c;后單調不增。 因為有這兩條條件&#xff0c;我們可以得知在判斷時只用判斷前半段的每個數是不是和對面相應的位置相等&#xff0c;以及是否單調不減。 為什么不用看后半段…

Unity Android 文件的讀寫

配置AndroidManifest 文件在Assets 目錄下查找AndroidManifest 文件&#xff0c;添加權限聲明&#xff0c;在application 節點中添加requestLegacyExternalStorage 屬性。<!-- 權限聲明 --> <uses-permission android:name"android.permission.READ_EXTERNAL_STO…

Pydantic模型驗證測試:你的API數據真的安全嗎?

url: /posts/03b2afdf35f55dbaef631710ab6da82c/ title: Pydantic模型驗證測試:你的API數據真的安全嗎? date: 2025-09-03T23:46:18+08:00 lastmod: 2025-09-03T23:46:18+08:00 author: cmdragon summary: Pydantic在FastAPI中用于數據驗證和序列化,通過Python類型注解自動…

【Proteus仿真】AT89C51單片機中斷系列仿真——INT0中斷控制LED小燈/INT0和INT1中斷控制數碼管

目錄 0案例視頻效果展示 0.1例子1&#xff1a;INT0控制LED閃爍 0.2例子2&#xff1a;INT0中斷控制數碼管計數 0.3例子3&#xff1a;INT0中斷實現秒表功能 0.4例子4&#xff1a;INT0INT1中斷控制數碼管計數 1基礎知識補充——中斷系統 1.1 中斷源一覽 1.2 控制寄存器 1…

MTK Linux DRM分析(三十三)- MTK mtk_mipi_tx.c

一、MIPI PHY驅動簡介 1. MIPI 協議分層 應用層:顯示(DSI)、攝像頭(CSI)。 協議層:定義像素/圖像幀如何封裝成數據包。 物理層(PHY):具體電氣信號傳輸方式 —— 這里就是 D-PHY 或 C-PHY。 2. D-PHY(Differential PHY) 傳輸方式:差分信號(類似 LVDS/USB/PCIe …

G2D 圖形加速器

文章目錄G2D 圖形加速器1. 功能簡介1.1 矩形填充1.2 旋轉和鏡像 (rotate and mirror)1.3 透明度混合1.4 colorkey1.5 縮放 (Stretchblt)2. G2D 框架3. 全志 G2D 使用示例3.1 使用G2D實現圖像旋轉縮放3.2 實時預覽中加入旋轉縮放功能G2D 圖形加速器 G2D模塊主要實現圖像旋轉、數…

【FPGA】單總線——DS18B20

目錄 項目&#xff1a;項目&#xff08;含quartus工程、仿真文件&#xff09; 1. 單總線通信時序詳解 1.1 初始化&#xff08;復位脈沖 存在脈沖&#xff09; 1.2 寫時隙&#xff08;寫“0”和寫“1”&#xff09; 1.3 讀時隙 2. DS18B20 暫存器與溫度數據格式 2.1 暫存…

JUC的安全并發包機制

目錄 1. Lock機制&#xff1a;明鎖控制 2. 柵欄機制(CyclicBarrier) 3. 閉鎖機制(CountDownLatch) 4. 信號量機制(Semaphore) 5. 無鎖機制 1. Lock機制&#xff1a;明鎖控制 Lock接口提供了比synchronized更靈活的鎖機制&#xff0c;屬于明鎖&#xff08;需要手動獲取和釋…

開源企業級快速開發平臺(JeecgBoot)

JeecgBoot 是一款基于 Spring Boot Vue 技術棧的開源企業級快速開發平臺&#xff0c;旨在通過「低代碼代碼生成」模式降低企業級應用的開發成本&#xff0c;提升開發效率。其核心定位是“開箱即用的中后臺解決方案”&#xff0c;覆蓋權限管理、表單報表、工作流、代碼生成等核…

探索 PostgreSQL 和 MySQL 之間的主要差異和相似之處,找到滿足您項目需求的最佳數據庫解決方案。

探索 PostgreSQL 和 MySQL 之間的主要差異和相似之處&#xff0c;找到滿足您項目需求的最佳數據庫解決方案。 探索 PostgreSQL 和 MySQL 之間的主要差異和相似之處&#xff0c;找到滿足您項目需求的最佳數據庫解決方案。 關系數據庫已經存在了很長時間。事實上&#xff0c;關系…

如何畫時序圖、流程圖、狀態流轉圖

如何畫時序圖、流程圖、狀態流轉圖流程圖符號約定時序圖元素交互框最佳實踐狀態流轉圖在研發或者寫技術方案的時候&#xff0c;我們經常會畫各種圖。圖比文字更加容易理解一些&#xff0c;那么如何畫出優秀好看的圖呢下面簡單介紹一些畫圖時需要注意的點 流程圖 流程圖是流程…

CSDN 與 掘金 高效學習指南

CSDN 和掘金&#xff08;juejin.cn&#xff09;是國內最活躍的技術社區&#xff0c;但信息量巨大、質量參差不齊。高效運用的關鍵是&#xff1a;從“被動瀏覽”轉向“主動獲取”&#xff0c;避免陷入“收藏一堆文章卻學不會”的陷阱。 以下是為你量身定制的CSDN 與 掘金 高效學…