簡(jiǎn)介 Vue.js 為開(kāi)發(fā)人員提供了豐富的功能,既能加快開(kāi)發(fā)速度,又能構(gòu)建健壯且高性能的應(yīng)用程序。
盡管這些功能有其優(yōu)勢(shì),但如果使用不當(dāng),也可能成為錯(cuò)誤的根源,導(dǎo)致開(kāi)發(fā)人員花費(fèi)大量時(shí)間進(jìn)行調(diào)試。錯(cuò)誤不僅影響開(kāi)發(fā)效率,還可能導(dǎo)致應(yīng)用程序性能下降,最終影響 Vue 應(yīng)用的整體表現(xiàn)。
我們可以從他人的錯(cuò)誤中汲取教訓(xùn),在保證應(yīng)用程序功能和性能的同時(shí),編寫(xiě)更加簡(jiǎn)潔的代碼。
本文中,我使用 Vite 創(chuàng)建了一個(gè)最小化的 Vue 應(yīng)用程序。你可以從這個(gè)倉(cāng)庫(kù) 克隆該項(xiàng)目。在本地安裝依賴(lài)后,運(yùn)行 pnpm i
安裝依賴(lài),并啟動(dòng)開(kāi)發(fā)服務(wù)器。
場(chǎng)景 1:在 v-for
循環(huán)中使用非唯一的 ID 該錯(cuò)誤通常在以下兩種情況中出現(xiàn):
對(duì)數(shù)組中的元素進(jìn)行排序。 循環(huán)中的組件具有內(nèi)部狀態(tài)。 為便于演示,以下是一個(gè)代碼片段:
async function fetchData ( ) { try { const dataFromApi = await axios.get( "https://dummyjson.com/recipes?page=1&limit=10&skip=10&select=name,image" ); const recipes = dataFromApi.data.recipes as Recipe[]; favoriteRecipeList.value = [...favoriteRecipeList.value, ...recipes]; isLoading.value = false ; } catch (e) { isLoading.value = false ; console .log(e); } }
該函數(shù)從源數(shù)據(jù)獲取信息,并填充到本地變量中,然后使用 v-for
循環(huán)渲染配方列表,同時(shí)考慮到 SingleRecipe
組件有自己的內(nèi)部狀態(tài)。
<template > <main class ="md:min-h-screen md:p-5 p-2" > <h1 class ="text-5xl" > Ouch, Mistakes.</h1 > <section v-if ="isLoading" class ="w-full h-72 flex flex-col items-center justify-center" > <p class ="text-3xl" > Loading...</p > </section > <section v-auto-animate v-else class ="w-full grid md:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-3 mt-16 relative mb-14" > <button :disabled ="!haveRecipes" @click ="shuffleRecipe" class ="px-4 py-1.5 absolute -top-16 right-5 bg-green-700 text-white" > Shuffle </button > <SingleRecipe v-for ="(item, index) in favoriteRecipeList" :key ="index" :recipe ="item" /> </section > </main > </template >
以下是該示例的結(jié)果:
由于我們將數(shù)組的索引作為循環(huán)的 key
,一切看起來(lái)正常。當(dāng)我們點(diǎn)擊“洗牌”按鈕時(shí),雖然配方的順序發(fā)生了變化,但預(yù)期的過(guò)渡效果卻沒(méi)有顯示。
原因在于,每當(dāng) key
值變化時(shí),「Vue 會(huì)使用這些 key
來(lái)強(qiáng)制重新渲染」 。然而,由于我們使用的是數(shù)組索引作為 key
,即使列表發(fā)生變化,索引也不會(huì)改變,因此 Vue 沒(méi)有重新渲染元素,過(guò)渡效果也就沒(méi)有觸發(fā)。
為了解決這個(gè)問(wèn)題,我們可以使用一個(gè)更具唯一性的值作為 key
,幫助 Vue 跟蹤列表中元素的變化:
<SingleRecipe v-for ="item in favoriteRecipeList" :key ="item.name" :recipe ="item" />
如圖所示,過(guò)渡效果現(xiàn)在已按預(yù)期正常工作。
場(chǎng)景 2:依賴(lài)非響應(yīng)式值 在使用瀏覽器 API(如本地存儲(chǔ)和地理位置)時(shí),開(kāi)發(fā)人員往往需要響應(yīng)式的功能。但傳統(tǒng)的瀏覽器 API 本身并不具備響應(yīng)式,因此開(kāi)發(fā)人員可能會(huì)誤用它們作為計(jì)算屬性的值。
以下是一個(gè)代碼示例,演示如何使用 ref
訪問(wèn)視頻元素的屬性。
<script setup lang ="ts" > import { ref, computed } from "vue" ;import PlayButton from "@/components/icons/PlayButton.vue" ;import PauseButton from "@/components/icons/PauseButton.vue" ;const videoPlayer = ref<HTMLVideoElement>();const playing = computed(() => !videoPlayer.value?.paused);</script >
我們使用計(jì)算屬性來(lái)跟蹤視頻的播放狀態(tài),并在模板中顯示對(duì)應(yīng)的播放/暫停狀態(tài)。
<template > <main class ="md:min-h-screen md:p-5 p-2" > <h1 class ="text-5xl" > Ouch, Mistakes. - Non reactive dependency.</h1 > <section class ="mt-16 relative" > <video src ="/shrek_meets_donkey.mp4" ref ="videoPlayer" class ="mx-auto h-96" /> <div v-auto-animate class ="inline-flex gap-3 absolute top-[48%] right-[46%]" > <button @click ="videoPlayer?.play()" v-if ="playing" class ="p-2 rounded-md bg-black" > <PlayButton /> </button > <button v-else @click ="videoPlayer?.pause()" class ="p-2 rounded-md bg-red-700" > <PauseButton /> </button > </div > </section > </main > </template >
然而,在這種情況下,控制按鈕的狀態(tài)并不是響應(yīng)式的:
這是因?yàn)橛?jì)算屬性依賴(lài)了一個(gè)非響應(yīng)式的值。
為了解決這個(gè)問(wèn)題,我們可以使用 Vueuse 庫(kù)。這個(gè)庫(kù)提供了一系列組合式 API,使瀏覽器 API 具備響應(yīng)式特性,避免了重復(fù)造輪子的麻煩。
例如,我們可以使用 useMediaControls
這個(gè)組合式 API,輕松為媒體控制添加響應(yīng)式支持:
<template > <main class ="md:min-h-screen md:p-5 p-2" > <h1 class ="text-5xl" > Ouch, Mistakes. - Non reactive dependency.</h1 > <section class ="mt-16 relative" > <video ref ="videoRef" class ="mx-auto h-96" /> <div v-auto-animate class ="inline-flex gap-3 absolute top-[48%] right-[46%]" > <button @click ="videoRef?.play()" v-if ="!videoPlaying" class ="p-2 rounded-md bg-black" > <PlayButton /> </button > <button v-else @click ="videoRef?.pause()" class ="p-2 rounded-md bg-red-700" > <PauseButton /> </button > </div > </section > </main > </template > <script setup lang ="ts" > import { ref, computed } from "vue" ;import { useMediaControls } from "@vueuse/core" ;import PlayButton from "@/components/icons/PlayButton.vue" ;import PauseButton from "@/components/icons/PauseButton.vue" ;const videoRef = ref();const { playing : videoPlaying} = useMediaControls(videoRef, { src : "/shrek_meets_donkey.mp4" , });</script >
如預(yù)期一樣,它正常工作,因?yàn)?nbsp;useMediaControls
提供了一個(gè)響應(yīng)式的 ref
,可以在模板中用于顯示視頻的播放狀態(tài)。
此外,useMediaControls
還提供了其他有用的屬性,以便開(kāi)發(fā)者對(duì)媒體進(jìn)行更多控制。
場(chǎng)景 3:替換響應(yīng)式值 使用 Vue 的響應(yīng)式 API 時(shí),需要特別小心,避免錯(cuò)誤地替換整個(gè)對(duì)象,從而失去響應(yīng)性。
以下是一個(gè)代碼示例,
<template > <main class ="md:min-h-screen md:p-5 p-2" > <h1 class ="text-5xl" > Ouch, Mistakes - Replacing reactive value</h1 > <section class ="mt-16 relative" > <button @click ="loadMoreBirds" class ="px-4 py-1.5 absolute -top-16 right-5 bg-green-700 text-white" > Add more bird </button > <h1 class ="text-3xl my-3" > Swift birds in Europe</h1 > <pre > {{ arrLength }}</pre > <h1 class ="text-3xl my-3" v-if ="isLoading" > Loading...</h1 > <ul v-else class ="list-disc pl-3" > <li v-for ="item in swiftBirdsInEurope" :key ="item" > {{ item }}</li > </ul > </section > </main > </template > <script setup lang ="ts" > import { reactive, ref, computed } from "vue" ;const isLoading = ref(false );let swiftBirdsInEurope = reactive([ "Alpine swift" , "Chimney swift" , "Pacific swift" , ]);const arrLength = computed(() => swiftBirdsInEurope.length)function loadMoreBirds ( ) { isLoading.value = true ; swiftBirdsInEurope = [ "Common swift" , "Little swift" , "Pallid swift" , "White-rumped swift" , ]; setTimeout(() => (isLoading.value = false ), 2000 ); }</script >
在 loadMoreBirds
函數(shù)中更新響應(yīng)式值為一個(gè)新的鳥(niǎo)類(lèi)列表。使用 Vue 開(kāi)發(fā)工具檢查時(shí),雖然值確實(shí)更新了,但計(jì)算屬性似乎沒(méi)有重新計(jì)算。
出現(xiàn)這個(gè)問(wèn)題的原因是我們?cè)谟眯聰?shù)組替換響應(yīng)式值時(shí),斷開(kāi)了與響應(yīng)式系統(tǒng)的連接。
這是 Vue 響應(yīng)式 API 的已知限制。響應(yīng)式 API 無(wú)法追蹤已替換的對(duì)象,導(dǎo)致視圖未更新。
為了解決這個(gè)問(wèn)題,我們建議使用 ref
來(lái)存儲(chǔ)數(shù)據(jù)。ref
允許數(shù)據(jù)的可變性,同時(shí)保留響應(yīng)性。它還能正確存儲(chǔ)原始值,這是它的一個(gè)優(yōu)勢(shì)。
總結(jié) 不當(dāng)使用 Vue API 可能會(huì)導(dǎo)致意想不到的錯(cuò)誤。開(kāi)發(fā)人員需要在特定情況下遵循適當(dāng)?shù)?API 使用規(guī)范,確保代碼的響應(yīng)性和應(yīng)用的性能。
原文地址:https://medium.com/@dimeji.ogunleye20/common-vuejs-development-mistakes-10877bdc591d
該文章在 2024/11/28 17:41:21 編輯過(guò)