Vue3 + Axios 实现一个精美天气组件(含实时与未来预报)
一、前言
在很多管理系统、信息看板、门户首页中,天气模块是一个常见的小组件。
它不仅能展示当前的气温、天气状况,还能提供未来几天的天气趋势,让用户对环境有更好的感知。
今天我们就用 Vue3 + Axios,实现一个能够自动定位并展示实时天气和未来预报的天气组件。
并且我们会配上相应的天气图标,让界面更美观。
二、效果预览
组件会自动获取当前城市的天气:
- 实时天气(温度 + 天气图标)
- 今日最高/最低温度
- 未来 5 天天气预报
- 自动根据天气文字匹配相应的图片
三、实现思路
数据来源
我们使用 心知天气 API(https://seniverse.com/)来获取天气数据,location=ip
参数可以根据用户的 IP 自动定位城市。技术栈
- Vue3(组合式 API)
- Axios(请求 API 数据)
- SCSS(美化界面)
- 图片资源(天气图标)
功能逻辑
- 用
axios.get
分别请求 实时天气 和 未来天气。 - 将获取的数据存入 Vue3 的
ref
响应式变量。 - 根据天气文字选择对应的图标(如“晴”、“小雨”、“暴雪”等)。
- 用
v-for
循环渲染未来天气列表。
- 用
四、代码实现
<template>
<div class="weather">
<!-- 地址信息 -->
<div class="address">
<img src="@/assets/index/weather/address.png" alt="" />
<span>{{ weatherData.location.name }}</span> <!-- 城市名称 -->
</div>
<!-- 当前气候信息 -->
<div class="climate">
<!-- 天气图标 -->
<img class="climate-icon" :src="getWeatherIcon(weatherData.now.text)" alt="" />
<!-- 温度 -->
<div class="wendu">{{ weatherData.now.temperature }}<span>℃</span></div>
<!-- 今日最高/最低气温 + 空气质量 -->
<div class="today">
<div class="label">
<span>今天</span>
<span>{{ text }}</span> <!-- 天气描述(如晴、阴、雨等) -->
</div>
<div class="value">
<span class="value-text">{{ future.daily[0].high }}/{{ future.daily[0].low }}℃</span>
<div class="quality">
<span class="quality-icon"></span>
<span class="quality-text">优</span> <!-- 空气质量(这里写死为优) -->
</div>
</div>
</div>
</div>
<!-- 未来几天天气 -->
<div class="future">
<template v-for="(item, index) in future.daily" :key="index">
<div class="future-item">
<!-- 天气图标(白天) -->
<img :src="getWeatherIcon(item.text_day)" alt="" />
<div class="future-box">
<div class="box-label">{{ parseTime(item.date, "{m}月{d}日") }}</div>
<div class="box-value">{{ item.high }}/{{ item.low }}℃</div>
</div>
</div>
<!-- 分隔线,最后一天不画 -->
<div class="future-line" v-if="index !== future.daily.length - 1"></div>
</template>
</div>
</div>
</template>
<script setup name="Weather">
import axios from "axios";
// 当前天气数据(实时)
const weatherData = ref({
location: {},
now: {},
});
// 未来天气数据(默认值避免渲染时报错)
const future = ref({
location: {},
daily: [
{
high: "30",
low: "10",
},
],
});
// 根据天气描述返回对应的图标路径
const getWeatherIcon = (text) => {
let img = "晴"; // 默认是晴天
switch (text) {
case "晴": img = "晴"; break;
case "暴雨": img = "暴雨"; break;
case "雷阵雨": img = "雷阵雨"; break;
case "雷阵雨伴有冰雹": img = "雷阵雨伴有冰雹"; break;
case "大暴雨": img = "特大暴雨"; break;
case "特大暴雨": img = "特大暴雨"; break;
case "雾": img = "雾"; break;
case "小雨":
case "中雨":
case "大雨": img = "下雨"; break;
case "阵雪": img = "阵雪"; break;
case "小雪":
case "中雪":
case "大雪": img = "雪"; break;
case "暴雪": img = "暴雪"; break;
case "风":
case "大风": img = "风"; break;
case "飓风":
case "热带风暴":
case "龙卷风": img = "风"; break;
case "雨夹雪": img = "雨夹雪"; break;
default: break;
}
// 返回图片路径(Vite 的 new URL 写法)
return new URL(`../../assets/index/weather/${img}.png`, import.meta.url).href;
};
// 获取天气数据(调用心知天气 API)
const getList = () => {
// 当前天气
axios.get("https://api.seniverse.com/v3/weather/now.json?key=SjyiLD_odjCGOsHoF&location=ip&language=zh-Hans&unit=c")
.then((res) => {
weatherData.value = res.data.results[0];
});
// 未来 5 天的天气
axios.get("https://api.seniverse.com/v3/weather/daily.json?key=SjyiLD_odjCGOsHoF&location=ip&language=zh-Hans&unit=c&start=0&days=5")
.then((res) => {
future.value = res.data.results[0];
});
};
// 组件加载时调用一次
getList();
</script>
<style lang="scss" scoped>
.weather {
width: 100%;
height: 100%;
padding: 5px 15px;
background: url("@/assets/index/weather/bg.png") no-repeat;
background-size: 100% 100%;
/* 地址部分样式 */
.address {
display: flex;
align-items: center;
img {
width: 14px;
height: 17px;
margin-right: 5px;
}
span {
font-family: PingFang SC;
font-size: 12px;
color: #cce6f8;
}
}
/* 当前气候部分样式 */
.climate {
display: flex;
align-items: center;
margin: 8px 0;
.climate-icon {
width: 60px;
margin-right: 5px;
}
.wendu {
font-size: 50px;
font-weight: 600;
color: #ffffff;
margin-right: 20px;
line-height: 1;
span {
font-size: 20px;
}
}
.today {
.label {
width: 75px;
font-size: 16px;
color: #ffffff;
display: flex;
justify-content: space-between;
}
.value {
display: flex;
align-items: center;
.value-text {
font-size: 18px;
color: #ffffff;
margin-right: 10px;
}
.quality {
padding: 0 10px;
height: 20px;
background: #67baee;
border-radius: 10px;
.quality-icon {
width: 9px;
height: 9px;
border-radius: 50%;
background: #51cfa4;
border: 1px solid #ffffff;
margin-right: 4px;
display: inline-block;
}
.quality-text {
font-size: 14px;
color: #ffffff;
}
}
}
}
}
/* 未来天气部分 */
.future {
display: flex;
justify-content: space-between;
align-items: center;
.future-item {
display: flex;
align-items: center;
img {
width: 36px;
height: 36px;
margin-right: 3px;
}
.future-box {
.box-label {
font-size: 12px;
color: #cce6f8;
}
.box-value {
font-size: 12px;
color: #ffffff;
}
}
}
/* 中间的竖线分隔 */
.future-line {
width: 1px;
height: 25px;
background: #2794ea;
margin: 0 auto;
}
}
}
/* 针对屏幕宽度小于 1650px 时的样式优化 */
@media screen and (max-width: 1650px) {
.weather .climate {
.wendu {
margin-right: 15px;
}
.today .value .quality {
padding: 0 6px;
}
}
}
</style>