我正在使用 Vue3 为我在学校的项目创建一个天气网络应用程序。它工作得很好,直到您搜索城市时它仍然为您提供有关最后渲染的城市的信息。假设我搜索俄亥俄州并单击搜索按钮,它就会呈现。现在,如果我搜索伦敦并单击搜索按钮,呈现的信息仍然是俄亥俄州的信息。刷新页面时,现在会呈现有关伦敦的正确信息。
我正在关注 John Komanicki 在 Vue3 上的系列文章。
这是我的 Navigation.vue 组件,其中包含搜索栏:
<template>
<header class="sticky top-0 shadow-md z-50 bg-background">
<nav class="px-24 flex items-center justify-between gap-3 text-text py-5 max-md:py-4 max-sm:px-2">
<RouterLink :to="{name: 'home'}">
<div class="flex items-center gap-3">
<i class="fa-solid fa-cloud-meatball fa-2xl text-primary"></i>
<p class="text-3xl font-semibold text-primary max-md:text-xl">ClearVue</p>
</div>
</RouterLink>
<!-- Search Bar -->
<div class="relative w-[46%]">
<input v-model="searchQuery" type="text" placeholder="Search for a City" class="py-2 px-2 w-full bg-transparent rounded-md border border-gray-300 focus:border-secondary focus:outline-none placeholder:text-sm placeholder:font-Lexend" @input="getSearchResults">
<div class="flex items-center justify-center absolute bg-accent w-9 h-9 right-[3px] top-[3px] rounded-[4px] cursor-pointer hover:opacity-80" @click="previewCityFromButton">
<i class="fa-solid fa-magnifying-glass text-white"></i>
</div>
<ul v-if="geoCodingSearchResults && showDropdown" class="absolute bg-gray-200 text-text w-full shadow-sm py-1 px-1 top-[44px] rounded-md">
<p v-if="searchError">Sorry, something went wrong. Please, try again!</p>
<p v-if="!searchError && geoCodingSearchResults.length === 0" class="py-1 px-1">No results found. Try a different City.</p>
<template v-else>
<li v-for="searchResult in geoCodingSearchResults" :key="searchResult.id" class="py-1 px-1 rounded-[4px] cursor-pointer hover:bg-gray-300 duration-150 ease-out" @click="pushSearchQuery(searchResult)">
{{ `${searchResult.name}, ${searchResult.state}, ${searchResult.country}` }}
</li>
</template>
</ul>
</div>
<!-- Nav Links -->
<ul class="flex items-center gap-8 font-semibold text-text max-md:text-sm max-sm:hidden">
<li class="hover:text-secondary duration-150 ease-out cursor-pointer" @click="openModal('about')">About ClearVue</li>
<li class="hover:text-secondary duration-150 ease-out cursor-pointer" @click="openModal('how')">How it works</li>
</ul>
<Modal :modalActive="modalActive" @close-modal="toggleModal">
<div class="text-text" v-if="selectedLink === 'about'">
<h1 class="text-xl mb-4 font-semibold">
About ClearVue:
</h1>
<p>ClearVue is a user-friendly and intuitive weather application designed to provide you with up-to-date weather information at your fingertips.<br><br> Whether you're planning your day, a trip, or simply want to stay informed about the weather conditions, ClearVue has you covered.</p>
</div>
<div class="text-text" v-if="selectedLink === 'how'">
<h1 class="text-xl mb-4 font-semibold">
How it works:
</h1>
<p>
ClearVue is a user-friendly weather application that provides accurate and real-time weather information. Here's how it works:
</p>
<ul>
<li>1. Start by entering a location to search for.</li>
<li>2. View current weather conditions, including temperature, wind speed, humidity, and more.</li>
<li>3. Track a City to view at a later time.</li>
</ul>
</div>
</Modal>
</nav>
</header>
</template>
<script setup>
import { RouterLink } from 'vue-router';
import Modal from './Modal.vue';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios';
// Modal scripts
const modalActive = ref(false);
const selectedLink = ref(null);
const openModal = (link) => {
selectedLink.value = link;
modalActive.value = true;
}
const toggleModal = () => {
modalActive.value = !modalActive.value;
};
// Input scripts
const searchQuery = ref("");
const queryTimeout = ref(null);
const geoCodingAPIKey = 'e7a0834d5c68a78d2a20c4ade200a188';
const geoCodingSearchResults = ref(null);
const searchError = ref(null);
const getSearchResults = () => {
clearTimeout(queryTimeout.value);
queryTimeout.value = setTimeout(async () => {
if (searchQuery.value !== "") {
try {
const result = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${searchQuery.value}&limit=5&appid=${geoCodingAPIKey}`);
geoCodingSearchResults.value = result.data;
} catch {
searchError.value = true;
}
return;
};
geoCodingSearchResults.value = null;
}, 200)
};
let lastSelectedSearchResult = ref(null);
const previewCityFromButton = () => {
if (lastSelectedSearchResult.value) {
previewCity(lastSelectedSearchResult.value);
}
searchQuery.value = ""
};
const showDropdown = ref(true);
const pushSearchQuery = (searchResult) => {
searchQuery.value = `${searchResult.name}, ${searchResult.state}, ${searchResult.country}`;
showDropdown.value = false;
lastSelectedSearchResult.value = searchResult;
};
const router = useRouter();
const previewCity = (searchResult) => {
console.log(searchResult);
const city = searchResult.name;
const state = searchResult.state;
router.push({
name: 'cityView',
params: { state: state, city: city },
query: {
lat: searchResult.lat,
lng: searchResult.lon,
preview: true
}
});
}
</script>
这是我的 AsyncCityView.vue 组件,其中呈现数据:
<template>
<div class="flex flex-col flex-1 items-center text-text">
<!-- Banner -->
<div v-if="route.query.preview && showBanner" class="flex items-center justify-end gap-[380px] text-text py-3 px-8 bg-blue-100 w-full text-center max-sm:gap-0">
<p>You are currently previewing this City. Click the "+" icon to track this City.</p>
<i class="fa-solid fa-circle-xmark cursor-pointer hover:opacity-80 duration-300 ease-out" @click="closeBanner"></i>
</div>
<!-- Overview -->
<div class="flex flex-col items-center text-text py-12">
<div class="flex relative">
<h1 class="text-4xl mb-4">{{ route.params.city }}</h1>
<i class="fa-solid fa-circle-plus text-xl absolute top-2 right-[-6rem] cursor-pointer text-accent hover:text-2xl duration-200 ease-in-out"></i>
</div>
<p class="text-9xl mb-6">
{{ Math.round(weatherData.data.main.temp) }}°
</p>
<p>
Feels like: {{ Math.round(weatherData.data.main.feels_like) }}°
</p>
<p class="capitalize">
{{ weatherData.data.weather[0].description }}
</p>
<img class="w-48 h-auto bg-secondaryLight rounded-full mt-6" :src="`http://openweathermap.org/img/wn/${weatherData.data.weather[0].icon}@2x.png`" alt="Image of Current Weather">
</div>
<hr class="border-text border-opacity-10 border w-[48rem]">
<div class="flex items-center gap-8 py-8">
<!-- Wind Readings -->
<div class="flex flex-col items-center">
<img class="w-10 h-auto p-2 bg-accent rounded-md m-2" src="../assets/img/wind.svg" alt="">
<p class="font-bold text-lg">Wind</p>
<p class="opacity-90"> {{ weatherData.data.wind.speed +'km/h' }}</p>
</div>
<!-- Humidity Readings -->
<div class="flex flex-col items-center">
<img class="w-10 h-auto p-2 bg-accent rounded-md m-2" src="../assets/img/humidity.svg" alt="">
<p class="font-bold text-lg">Humidity</p>
<p class="opacity-90"> {{ weatherData.data.main.humidity +'%' }}</p>
</div>
<!-- Pressure Readings -->
<div class="flex flex-col items-center">
<img class="w-10 h-auto p-2 bg-accent rounded-md m-2" src="../assets/img/pessure.svg" alt="">
<p class="font-bold text-lg">Pressure</p>
<p class="opacity-90"> {{ weatherData.data.main.pressure +'hPa' }}</p>
</div>
</div>
</div>
</template>
<script setup>
import axios from 'axios';
import { useRoute } from 'vue-router';
import { ref } from 'vue';
const showBanner = ref(true);
const closeBanner = () => {
showBanner.value = false;
};
const apiKey = 'e7a0834d5c68a78d2a20c4ade200a188';
const route = useRoute();
const getWeatherData = async () => {
try {
const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${route.query.lat}&lon=${route.query.lng}&appid=${apiKey}&units=metric`);
return weatherData;
} catch(err) {
console.log(err);
}
}
const weatherData = await getWeatherData();
console.log(weatherData);
</script>
由于我不精通Vue3,所以我向ChatGPT寻求帮助。没练过。
首先:你应该阅读 Vue 3 的生命周期钩子。 下一步:隐藏您的令牌,它必须是秘密的。 您的搜索应该是搜索、保存、清除。对于每个请求都是如此。
例如看这个项目代码