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>