簡介
Vue.js 為開發(fā)人員提供了豐富的功能,既能加快開發(fā)速度,又能構(gòu)建健壯且高性能的應(yīng)用程序。
盡管這些功能有其優(yōu)勢,但如果使用不當(dāng),也可能成為錯(cuò)誤的根源,導(dǎo)致開發(fā)人員花費(fèi)大量時(shí)間進(jìn)行調(diào)試。錯(cuò)誤不僅影響開發(fā)效率,還可能導(dǎo)致應(yīng)用程序性能下降,最終影響 Vue 應(yīng)用的整體表現(xiàn)。
我們可以從他人的錯(cuò)誤中汲取教訓(xùn),在保證應(yīng)用程序功能和性能的同時(shí),編寫更加簡潔的代碼。
本文中,我使用 Vite 創(chuàng)建了一個(gè)最小化的 Vue 應(yīng)用程序。你可以從這個(gè)倉庫克隆該項(xiàng)目。在本地安裝依賴后,運(yùn)行 pnpm i
安裝依賴,并啟動(dòng)開發(fā)服務(wù)器。
場景 1:在 v-for
循環(huán)中使用非唯一的 ID
該錯(cuò)誤通常在以下兩種情況中出現(xià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
,一切看起來正常。當(dāng)我們點(diǎn)擊“洗牌”按鈕時(shí),雖然配方的順序發(fā)生了變化,但預(yù)期的過渡效果卻沒有顯示。
原因在于,每當(dāng) key
值變化時(shí),「Vue 會(huì)使用這些 key
來強(qiáng)制重新渲染」。然而,由于我們使用的是數(shù)組索引作為 key
,即使列表發(fā)生變化,索引也不會(huì)改變,因此 Vue 沒有重新渲染元素,過渡效果也就沒有觸發(fā)。
為了解決這個(gè)問題,我們可以使用一個(gè)更具唯一性的值作為 key
,幫助 Vue 跟蹤列表中元素的變化:
<SingleRecipe
v-for="item in favoriteRecipeList"
:key="item.name"
:recipe="item"
/>
如圖所示,過渡效果現(xiàn)在已按預(yù)期正常工作。
場景 2:依賴非響應(yīng)式值
在使用瀏覽器 API(如本地存儲(chǔ)和地理位置)時(shí),開發(fā)人員往往需要響應(yīng)式的功能。但傳統(tǒng)的瀏覽器 API 本身并不具備響應(yīng)式,因此開發(fā)人員可能會(huì)誤用它們作為計(jì)算屬性的值。
以下是一個(gè)代碼示例,演示如何使用 ref
訪問視頻元素的屬性。
<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ì)算屬性來跟蹤視頻的播放狀態(tài),并在模板中顯示對應(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ì)算屬性依賴了一個(gè)非響應(yīng)式的值。
為了解決這個(gè)問題,我們可以使用 Vueuse 庫。這個(gè)庫提供了一系列組合式 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
還提供了其他有用的屬性,以便開發(fā)者對媒體進(jìn)行更多控制。
場景 3:替換響應(yīng)式值
使用 Vue 的響應(yīng)式 API 時(shí),需要特別小心,避免錯(cuò)誤地替換整個(gè)對象,從而失去響應(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è)新的鳥類列表。使用 Vue 開發(fā)工具檢查時(shí),雖然值確實(shí)更新了,但計(jì)算屬性似乎沒有重新計(jì)算。
出現(xiàn)這個(gè)問題的原因是我們在用新數(shù)組替換響應(yīng)式值時(shí),斷開了與響應(yīng)式系統(tǒng)的連接。
這是 Vue 響應(yīng)式 API 的已知限制。響應(yīng)式 API 無法追蹤已替換的對象,導(dǎo)致視圖未更新。
為了解決這個(gè)問題,我們建議使用 ref
來存儲(chǔ)數(shù)據(jù)。ref
允許數(shù)據(jù)的可變性,同時(shí)保留響應(yīng)性。它還能正確存儲(chǔ)原始值,這是它的一個(gè)優(yōu)勢。
總結(jié)
不當(dāng)使用 Vue API 可能會(huì)導(dǎo)致意想不到的錯(cuò)誤。開發(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 編輯過